| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| |
| package cloudstack |
| |
| import ( |
| "fmt" |
| "log" |
| "strings" |
| |
| "github.com/apache/cloudstack-go/v2/cloudstack" |
| "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" |
| ) |
| |
| func resourceCloudStackVPCOffering() *schema.Resource { |
| return &schema.Resource{ |
| Create: resourceCloudStackVPCOfferingCreate, |
| Read: resourceCloudStackVPCOfferingRead, |
| Update: resourceCloudStackVPCOfferingUpdate, |
| Delete: resourceCloudStackVPCOfferingDelete, |
| Importer: &schema.ResourceImporter{ |
| State: importStatePassthrough, |
| }, |
| Schema: map[string]*schema.Schema{ |
| "name": { |
| Type: schema.TypeString, |
| Required: true, |
| }, |
| "display_text": { |
| Type: schema.TypeString, |
| Required: true, |
| }, |
| "domain_id": { |
| Type: schema.TypeList, |
| Elem: &schema.Schema{Type: schema.TypeString}, |
| Optional: true, |
| Description: "the ID of the containing domain(s), null for public offerings", |
| ForceNew: true, |
| }, |
| "zone_id": { |
| Type: schema.TypeList, |
| Elem: &schema.Schema{Type: schema.TypeString}, |
| Optional: true, |
| Description: "the ID of the containing zone(s), null for offerings available in all zones", |
| ForceNew: true, |
| }, |
| "enable": { |
| Type: schema.TypeBool, |
| Optional: true, |
| Description: "set to true if the offering is to be enabled during creation. Default is false", |
| }, |
| "for_nsx": { |
| Type: schema.TypeBool, |
| Optional: true, |
| Description: "true if the VPC offering is meant to be used for NSX, false otherwise", |
| ForceNew: true, |
| }, |
| "nsx_support_lb": { |
| Type: schema.TypeBool, |
| Optional: true, |
| Description: "true if the NSX supports Lb service, false otherwise", |
| ForceNew: true, |
| }, |
| "internet_protocol": { |
| Type: schema.TypeString, |
| Optional: true, |
| Computed: true, |
| Description: "The internet protocol of the VPC offering. Options are ipv4 and dualstack. Default is ipv4. dualstack will create a VPC offering that supports both IPv4 and IPv6", |
| ForceNew: true, |
| DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { |
| return strings.EqualFold(old, new) |
| }, |
| }, |
| "network_mode": { |
| Type: schema.TypeString, |
| Optional: true, |
| Computed: true, |
| Description: "Indicates the mode with which the VPC will operate. Valid option: NATTED or ROUTED", |
| ForceNew: true, |
| }, |
| "routing_mode": { |
| Type: schema.TypeString, |
| Optional: true, |
| Computed: true, |
| Description: "the routing mode for the VPC offering. Supported types are: Static or Dynamic.", |
| ForceNew: true, |
| DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { |
| return strings.EqualFold(old, new) |
| }, |
| }, |
| "specify_as_number": { |
| Type: schema.TypeBool, |
| Optional: true, |
| Description: "true if the VPC offering supports choosing AS number", |
| ForceNew: true, |
| }, |
| "network_provider": { |
| Type: schema.TypeString, |
| Optional: true, |
| Description: "network provider for the VPC offering", |
| ForceNew: true, |
| }, |
| "service_offering_id": { |
| Type: schema.TypeString, |
| Optional: true, |
| Description: "the ID of the service offering for the VPC router appliance", |
| ForceNew: true, |
| }, |
| "supported_services": { |
| Type: schema.TypeSet, |
| Elem: &schema.Schema{Type: schema.TypeString}, |
| Required: true, |
| Description: "the list of supported services", |
| ForceNew: true, |
| }, |
| "service_provider_list": { |
| Type: schema.TypeMap, |
| Optional: true, |
| Computed: true, |
| Description: "provider to service mapping. If not specified, the provider for the service will be mapped to the default provider on the physical network", |
| ForceNew: true, |
| }, |
| "service_capability_list": { |
| Type: schema.TypeList, |
| Optional: true, |
| Computed: true, |
| ForceNew: true, |
| Description: "desired service capabilities as part of the VPC offering", |
| Elem: &schema.Resource{ |
| Schema: map[string]*schema.Schema{ |
| "service": { |
| Type: schema.TypeString, |
| Required: true, |
| Description: "the service the capability applies to, e.g. SourceNat", |
| }, |
| "capability_type": { |
| Type: schema.TypeString, |
| Required: true, |
| Description: "the capability type, e.g. RedundantRouter", |
| }, |
| "capability_value": { |
| Type: schema.TypeString, |
| Required: true, |
| Description: "the capability value, e.g. true", |
| }, |
| }, |
| }, |
| }, |
| "is_default": { |
| Type: schema.TypeBool, |
| Computed: true, |
| Description: "true if this is the default VPC offering", |
| }, |
| }, |
| } |
| } |
| |
| func resourceCloudStackVPCOfferingCreate(d *schema.ResourceData, meta interface{}) error { |
| cs := meta.(*cloudstack.CloudStackClient) |
| name := d.Get("name").(string) |
| displayText := d.Get("display_text").(string) |
| |
| // Create a new parameter struct |
| p := cs.VPC.NewCreateVPCOfferingParams(displayText, name) |
| |
| if v, ok := d.GetOk("domain_id"); ok { |
| p.SetDomainid(expandStringList(v.([]interface{}))) |
| } |
| |
| if v, ok := d.GetOk("zone_id"); ok { |
| p.SetZoneid(expandStringList(v.([]interface{}))) |
| } |
| |
| if v, ok := d.GetOk("enable"); ok { |
| p.SetEnable(v.(bool)) |
| } |
| |
| if v, ok := d.GetOk("for_nsx"); ok { |
| p.SetFornsx(v.(bool)) |
| } |
| |
| if v, ok := d.GetOk("nsx_support_lb"); ok { |
| p.SetNsxsupportlb(v.(bool)) |
| } |
| |
| if v, ok := d.GetOk("internet_protocol"); ok { |
| p.SetInternetprotocol(v.(string)) |
| } |
| |
| if v, ok := d.GetOk("network_mode"); ok { |
| p.SetNetworkmode(v.(string)) |
| } |
| |
| if v, ok := d.GetOk("routing_mode"); ok { |
| p.SetRoutingmode(v.(string)) |
| } |
| |
| if v, ok := d.GetOk("specify_as_number"); ok { |
| p.SetSpecifyasnumber(v.(bool)) |
| } |
| |
| if v, ok := d.GetOk("network_provider"); ok { |
| p.SetProvider(v.(string)) |
| } |
| |
| if v, ok := d.GetOk("service_offering_id"); ok { |
| serviceOfferingID, e := retrieveID(cs, "service_offering", v.(string)) |
| if e != nil { |
| return e.Error() |
| } |
| p.SetServiceofferingid(serviceOfferingID) |
| } |
| |
| if v, ok := d.GetOk("supported_services"); ok { |
| var supportedServices []string |
| for _, service := range v.(*schema.Set).List() { |
| supportedServices = append(supportedServices, service.(string)) |
| } |
| p.SetSupportedservices(supportedServices) |
| } |
| |
| if v, ok := d.GetOk("service_provider_list"); ok { |
| m := make(map[string]string) |
| for key, value := range v.(map[string]interface{}) { |
| m[key] = value.(string) |
| } |
| p.SetServiceproviderlist(m) |
| } |
| |
| if v, ok := d.GetOk("service_capability_list"); ok { |
| var capabilities []map[string]string |
| for _, item := range v.([]interface{}) { |
| c := item.(map[string]interface{}) |
| capabilities = append(capabilities, map[string]string{ |
| "service": c["service"].(string), |
| "capabilitytype": c["capability_type"].(string), |
| "capabilityvalue": c["capability_value"].(string), |
| }) |
| } |
| p.SetServicecapabilitylist(capabilities) |
| } |
| |
| log.Printf("[DEBUG] Creating VPC Offering %s", name) |
| o, err := cs.VPC.CreateVPCOffering(p) |
| if err != nil { |
| return fmt.Errorf("Error creating VPC Offering %s: %s", name, err) |
| } |
| |
| d.SetId(o.Id) |
| |
| log.Printf("[DEBUG] VPC Offering %s successfully created", name) |
| return resourceCloudStackVPCOfferingRead(d, meta) |
| } |
| |
| func resourceCloudStackVPCOfferingRead(d *schema.ResourceData, meta interface{}) error { |
| cs := meta.(*cloudstack.CloudStackClient) |
| log.Printf("[DEBUG] Retrieving VPC Offering %s", d.Id()) |
| |
| // Get the VPC Offering details |
| o, count, err := cs.VPC.GetVPCOfferingByID(d.Id()) |
| if err != nil { |
| if count == 0 { |
| log.Printf("[DEBUG] VPC Offering %s does no longer exist", d.Id()) |
| d.SetId("") |
| return nil |
| } |
| |
| return err |
| } |
| |
| d.SetId(o.Id) |
| d.Set("name", o.Name) |
| d.Set("display_text", o.Displaytext) |
| d.Set("enable", o.State == "Enabled") |
| d.Set("is_default", o.Isdefault) |
| |
| d.Set("for_nsx", o.Fornsx) |
| d.Set("specify_as_number", o.Specifyasnumber) |
| |
| if o.Internetprotocol != "" { |
| d.Set("internet_protocol", o.Internetprotocol) |
| } |
| |
| if o.Networkmode != "" { |
| d.Set("network_mode", o.Networkmode) |
| } |
| |
| if o.Routingmode != "" { |
| d.Set("routing_mode", o.Routingmode) |
| } |
| |
| if o.Domainid != "" { |
| d.Set("domain_id", []string{o.Domainid}) |
| } |
| |
| if o.Zoneid != "" { |
| d.Set("zone_id", []string{o.Zoneid}) |
| } |
| |
| if len(o.Service) > 0 { |
| services := make([]string, len(o.Service)) |
| serviceProviders := make(map[string]string) |
| var capabilities []map[string]interface{} |
| for i, service := range o.Service { |
| services[i] = service.Name |
| if len(service.Provider) > 0 { |
| serviceProviders[service.Name] = service.Provider[0].Name |
| } |
| for _, capability := range service.Capability { |
| capabilities = append(capabilities, map[string]interface{}{ |
| "service": service.Name, |
| "capability_type": capability.Name, |
| "capability_value": capability.Value, |
| }) |
| } |
| } |
| d.Set("supported_services", services) |
| d.Set("service_provider_list", serviceProviders) |
| if len(capabilities) > 0 { |
| d.Set("service_capability_list", capabilities) |
| } |
| } |
| |
| return nil |
| } |
| |
| func resourceCloudStackVPCOfferingUpdate(d *schema.ResourceData, meta interface{}) error { |
| cs := meta.(*cloudstack.CloudStackClient) |
| name := d.Get("name").(string) |
| |
| // Check if the name is changed and if so, update the VPC offering |
| if d.HasChange("name") { |
| log.Printf("[DEBUG] Name changed for %s, starting update", name) |
| |
| p := cs.VPC.NewUpdateVPCOfferingParams(d.Id()) |
| p.SetName(name) |
| |
| _, err := cs.VPC.UpdateVPCOffering(p) |
| if err != nil { |
| return fmt.Errorf("Error updating the name for VPC offering %s: %s", name, err) |
| } |
| } |
| |
| // Check if the display text is changed and if so, update the VPC offering |
| if d.HasChange("display_text") { |
| log.Printf("[DEBUG] Display text changed for %s, starting update", name) |
| |
| p := cs.VPC.NewUpdateVPCOfferingParams(d.Id()) |
| p.SetDisplaytext(d.Get("display_text").(string)) |
| |
| _, err := cs.VPC.UpdateVPCOffering(p) |
| if err != nil { |
| return fmt.Errorf("Error updating the display text for VPC offering %s: %s", name, err) |
| } |
| } |
| |
| // Check if the enabled state is changed and if so, update the VPC offering |
| if d.HasChange("enable") { |
| log.Printf("[DEBUG] State changed for %s, starting update", name) |
| |
| state := "Disabled" |
| if d.Get("enable").(bool) { |
| state = "Enabled" |
| } |
| |
| p := cs.VPC.NewUpdateVPCOfferingParams(d.Id()) |
| p.SetState(state) |
| |
| _, err := cs.VPC.UpdateVPCOffering(p) |
| if err != nil { |
| return fmt.Errorf("Error updating the state for VPC offering %s: %s", name, err) |
| } |
| } |
| |
| return resourceCloudStackVPCOfferingRead(d, meta) |
| } |
| |
| func resourceCloudStackVPCOfferingDelete(d *schema.ResourceData, meta interface{}) error { |
| cs := meta.(*cloudstack.CloudStackClient) |
| |
| // Create a new parameter struct |
| p := cs.VPC.NewDeleteVPCOfferingParams(d.Id()) |
| _, err := cs.VPC.DeleteVPCOffering(p) |
| if err != nil { |
| return fmt.Errorf("Error deleting VPC Offering: %s", err) |
| } |
| |
| return nil |
| } |
| |
| func expandStringList(list []interface{}) []string { |
| result := make([]string, len(list)) |
| for i, v := range list { |
| result[i] = v.(string) |
| } |
| return result |
| } |