diff --git a/huaweicloud/data_source_huaweicloud_networking_port_v2.go b/huaweicloud/data_source_huaweicloud_networking_port_v2.go new file mode 100644 index 0000000000..374db89b83 --- /dev/null +++ b/huaweicloud/data_source_huaweicloud_networking_port_v2.go @@ -0,0 +1,237 @@ +package huaweicloud + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + + "github.com/huaweicloud/golangsdk/openstack/networking/v2/ports" +) + +func dataSourceNetworkingPortV2() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNetworkingPortV2Read, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + }, + + "port_id": { + Type: schema.TypeString, + Optional: true, + }, + + "name": { + Type: schema.TypeString, + Optional: true, + }, + + "admin_state_up": { + Type: schema.TypeBool, + Optional: true, + }, + + "network_id": { + Type: schema.TypeString, + Optional: true, + }, + + "tenant_id": { + Type: schema.TypeString, + Optional: true, + }, + + "project_id": { + Type: schema.TypeString, + Optional: true, + }, + + "device_owner": { + Type: schema.TypeString, + Optional: true, + }, + + "mac_address": { + Type: schema.TypeString, + Optional: true, + }, + + "device_id": { + Type: schema.TypeString, + Optional: true, + }, + + "fixed_ip": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.SingleIP(), + }, + + "status": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "security_group_ids": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + + "all_fixed_ips": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "all_security_group_ids": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + }, + } +} + +func dataSourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(GetRegion(d, config)) + if err != nil { + return fmt.Errorf("Error creating HuaweiCloud networking client: %s", err) + } + + listOpts := ports.ListOpts{} + + if v, ok := d.GetOk("port_id"); ok { + listOpts.ID = v.(string) + } + + if v, ok := d.GetOk("name"); ok { + listOpts.Name = v.(string) + } + + if v, ok := d.GetOkExists("admin_state_up"); ok { + asu := v.(bool) + listOpts.AdminStateUp = &asu + } + + if v, ok := d.GetOk("network_id"); ok { + listOpts.NetworkID = v.(string) + } + + if v, ok := d.GetOk("status"); ok { + listOpts.Status = v.(string) + } + + if v, ok := d.GetOk("tenant_id"); ok { + listOpts.TenantID = v.(string) + } + + if v, ok := d.GetOk("project_id"); ok { + listOpts.ProjectID = v.(string) + } + + if v, ok := d.GetOk("device_owner"); ok { + listOpts.DeviceOwner = v.(string) + } + + if v, ok := d.GetOk("mac_address"); ok { + listOpts.MACAddress = v.(string) + } + + if v, ok := d.GetOk("device_id"); ok { + listOpts.DeviceID = v.(string) + } + + allPages, err := ports.List(networkingClient, listOpts).AllPages() + if err != nil { + return fmt.Errorf("Unable to list huaweicloud_networking_ports_v2: %s", err) + } + + var allPorts []ports.Port + + err = ports.ExtractPortsInto(allPages, &allPorts) + if err != nil { + return fmt.Errorf("Unable to retrieve huaweicloud_networking_ports_v2: %s", err) + } + + if len(allPorts) == 0 { + return fmt.Errorf("No huaweicloud_networking_port_v2 found") + } + + var portsList []ports.Port + + // Filter returned Fixed IPs by a "fixed_ip". + if v, ok := d.GetOk("fixed_ip"); ok { + for _, p := range allPorts { + for _, ipObject := range p.FixedIPs { + if v.(string) == ipObject.IPAddress { + portsList = append(portsList, p) + } + } + } + if len(portsList) == 0 { + log.Printf("No huaweicloud_networking_port_v2 found after the 'fixed_ip' filter") + return fmt.Errorf("No huaweicloud_networking_port_v2 found") + } + } else { + portsList = allPorts + } + + securityGroups := expandToStringSlice(d.Get("security_group_ids").(*schema.Set).List()) + if len(securityGroups) > 0 { + var sgPorts []ports.Port + for _, p := range portsList { + for _, sg := range p.SecurityGroups { + if strSliceContains(securityGroups, sg) { + sgPorts = append(sgPorts, p) + } + } + } + if len(sgPorts) == 0 { + log.Printf("[DEBUG] No huaweicloud_networking_port_v2 found after the 'security_group_ids' filter") + return fmt.Errorf("No huaweicloud_networking_port_v2 found") + } + portsList = sgPorts + } + + if len(portsList) > 1 { + return fmt.Errorf("More than one huaweicloud_networking_port_v2 found (%d)", len(portsList)) + } + + port := portsList[0] + + log.Printf("[DEBUG] Retrieved huaweicloud_networking_port_v2 %s: %+v", port.ID, port) + d.SetId(port.ID) + + d.Set("port_id", port.ID) + d.Set("name", port.Name) + d.Set("admin_state_up", port.AdminStateUp) + d.Set("network_id", port.NetworkID) + d.Set("tenant_id", port.TenantID) + d.Set("project_id", port.ProjectID) + d.Set("device_owner", port.DeviceOwner) + d.Set("mac_address", port.MACAddress) + d.Set("device_id", port.DeviceID) + d.Set("region", GetRegion(d, config)) + d.Set("all_security_group_ids", port.SecurityGroups) + d.Set("all_fixed_ips", expandNetworkingPortFixedIPToStringSlice(port.FixedIPs)) + + return nil +} + +func expandNetworkingPortFixedIPToStringSlice(fixedIPs []ports.IP) []string { + s := make([]string, len(fixedIPs)) + for i, fixedIP := range fixedIPs { + s[i] = fixedIP.IPAddress + } + + return s +} diff --git a/huaweicloud/data_source_huaweicloud_networking_port_v2_test.go b/huaweicloud/data_source_huaweicloud_networking_port_v2_test.go new file mode 100644 index 0000000000..8fd7c0dd40 --- /dev/null +++ b/huaweicloud/data_source_huaweicloud_networking_port_v2_test.go @@ -0,0 +1,93 @@ +package huaweicloud + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccNetworkingV2PortDataSource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2PortDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetworkingV2PortDataSource_basic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair( + "data.huaweicloud_networking_port_v2.port_1", "id", + "huaweicloud_networking_port_v2.port_1", "id"), + resource.TestCheckResourceAttrPair( + "data.huaweicloud_networking_port_v2.port_2", "id", + "huaweicloud_networking_port_v2.port_2", "id"), + resource.TestCheckResourceAttrPair( + "data.huaweicloud_networking_port_v2.port_3", "id", + "huaweicloud_networking_port_v2.port_1", "id"), + resource.TestCheckResourceAttr( + "data.huaweicloud_networking_port_v2.port_3", "all_fixed_ips.#", "1"), + ), + }, + }, + }) +} + +const testAccNetworkingV2PortDataSource_basic = ` +resource "huaweicloud_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} + +resource "huaweicloud_networking_subnet_v2" "subnet_1" { + name = "subnet_1" + network_id = "${huaweicloud_networking_network_v2.network_1.id}" + cidr = "10.0.0.0/24" + ip_version = 4 +} + +data "huaweicloud_networking_secgroup_v2" "default" { + name = "default" +} + +resource "huaweicloud_networking_port_v2" "port_1" { + name = "port_1" + network_id = "${huaweicloud_networking_network_v2.network_1.id}" + admin_state_up = "true" + + security_group_ids = [ + "${data.huaweicloud_networking_secgroup_v2.default.id}", + ] + + fixed_ip { + subnet_id = "${huaweicloud_networking_subnet_v2.subnet_1.id}" + } +} + +resource "huaweicloud_networking_port_v2" "port_2" { + name = "port_2" + network_id = "${huaweicloud_networking_network_v2.network_1.id}" + admin_state_up = "true" + + security_group_ids = [ + "${data.huaweicloud_networking_secgroup_v2.default.id}", + ] +} + +data "huaweicloud_networking_port_v2" "port_1" { + name = "${huaweicloud_networking_port_v2.port_1.name}" + admin_state_up = "${huaweicloud_networking_port_v2.port_1.admin_state_up}" + + security_group_ids = [ + "${data.huaweicloud_networking_secgroup_v2.default.id}", + ] +} + +data "huaweicloud_networking_port_v2" "port_2" { + name = "${huaweicloud_networking_port_v2.port_2.name}" + admin_state_up = "${huaweicloud_networking_port_v2.port_2.admin_state_up}" +} + +data "huaweicloud_networking_port_v2" "port_3" { + fixed_ip = "${huaweicloud_networking_port_v2.port_1.all_fixed_ips.0}" +} +` diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 11d47e1005..47fc121bda 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -193,6 +193,7 @@ func Provider() terraform.ResourceProvider { "huaweicloud_images_image_v2": dataSourceImagesImageV2(), "huaweicloud_networking_network_v2": dataSourceNetworkingNetworkV2(), "huaweicloud_networking_subnet_v2": dataSourceNetworkingSubnetV2(), + "huaweicloud_networking_port_v2": dataSourceNetworkingPortV2(), "huaweicloud_networking_secgroup_v2": dataSourceNetworkingSecGroupV2(), "huaweicloud_s3_bucket_object": dataSourceS3BucketObject(), "huaweicloud_kms_key_v1": dataSourceKmsKeyV1(), diff --git a/huaweicloud/util.go b/huaweicloud/util.go index 62ae626fb6..9561e7df22 100644 --- a/huaweicloud/util.go +++ b/huaweicloud/util.go @@ -155,6 +155,17 @@ func resourceNetworkingAvailabilityZoneHintsV2(d *schema.ResourceData) []string return azh } +func expandToStringSlice(v []interface{}) []string { + s := make([]string, len(v)) + for i, val := range v { + if strVal, ok := val.(string); ok { + s[i] = strVal + } + } + + return s +} + // strSliceContains checks if a given string is contained in a slice // When anybody asks why Go needs generics, here you go. func strSliceContains(haystack []string, needle string) bool {