From 77a41ca8596732a72ad79e89c916b22a42d814a2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 27 Mar 2017 11:22:56 -0600 Subject: [PATCH] provider/openstack: Resolve issues with Port Fixed IPs (#13056) * provider/openstack: Add all_fixed_ips computed attribute to port resource This commit adds the `all_fixed_ips` attribute to the openstack_networking_port_v2 resource. This contains all of the port's Fixed IPs returned by the Networking v2 API. * provider/openstack: Revert Port fixed_ip back to a List This commit reverts the changes made in a8c4e81a6e3f2. This re-enables the ability to reference IP addresses using the numerical-element notation. This commit also makes fixed_ip a non-computed field, meaning Terraform will no longer set fixed_ip with what was returned by the API. This resolves the original issue about ordering. The last use-case is for fixed_ips that received an IP address via DHCP. Because fixed_ip is no longer computed, the DHCP IP will not be set. The workaround for this use-case is to use the new all_fixed_ips attribute. In effect, users should use fixed_ip only as a way of inputting data into Terraform and use all_fixed_ips as the output returned by the API. If use-cases exist where fixed_ip can be used as an output, that's a bonus feature. --- .../resource_openstack_networking_port_v2.go | 24 ++-- ...ource_openstack_networking_port_v2_test.go | 116 ++++++++++++++++++ .../r/networking_port_v2.html.markdown | 4 +- 3 files changed, 132 insertions(+), 12 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_networking_port_v2.go b/builtin/providers/openstack/resource_openstack_networking_port_v2.go index aea9cb8dd..508ebc813 100644 --- a/builtin/providers/openstack/resource_openstack_networking_port_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_port_v2.go @@ -85,10 +85,9 @@ func resourceNetworkingPortV2() *schema.Resource { Computed: true, }, "fixed_ip": &schema.Schema{ - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, ForceNew: false, - Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "subnet_id": &schema.Schema{ @@ -98,7 +97,6 @@ func resourceNetworkingPortV2() *schema.Resource { "ip_address": &schema.Schema{ Type: schema.TypeString, Optional: true, - Computed: true, }, }, }, @@ -128,6 +126,11 @@ func resourceNetworkingPortV2() *schema.Resource { Optional: true, ForceNew: true, }, + "all_fixed_ips": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, }, } } @@ -202,15 +205,14 @@ func resourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) erro d.Set("security_group_ids", p.SecurityGroups) d.Set("device_id", p.DeviceID) - // Convert FixedIPs to list of map - var ips []map[string]interface{} + // Create a slice of all returned Fixed IPs. + // This will be in the order returned by the API, + // which is usually alpha-numeric. + var ips []string for _, ipObject := range p.FixedIPs { - ip := make(map[string]interface{}) - ip["subnet_id"] = ipObject.SubnetID - ip["ip_address"] = ipObject.IPAddress - ips = append(ips, ip) + ips = append(ips, ipObject.IPAddress) } - d.Set("fixed_ip", ips) + d.Set("all_fixed_ips", ips) // Convert AllowedAddressPairs to list of map var pairs []map[string]interface{} @@ -309,7 +311,7 @@ func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string { } func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} { - rawIP := d.Get("fixed_ip").(*schema.Set).List() + rawIP := d.Get("fixed_ip").([]interface{}) if len(rawIP) == 0 { return nil diff --git a/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go index ec731fe7a..28e08bebd 100644 --- a/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go @@ -50,6 +50,30 @@ func TestAccNetworkingV2Port_noip(t *testing.T) { testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet), testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network), testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port), + testAccCheckNetworkingV2PortCountFixedIPs(&port, 1), + ), + }, + }, + }) +} + +func TestAccNetworkingV2Port_multipleNoIP(t *testing.T) { + var network networks.Network + var port ports.Port + var subnet subnets.Subnet + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2PortDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNetworkingV2Port_multipleNoIP, + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet), + testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network), + testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port), + testAccCheckNetworkingV2PortCountFixedIPs(&port, 3), ), }, }, @@ -96,6 +120,7 @@ func TestAccNetworkingV2Port_multipleFixedIPs(t *testing.T) { testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet), testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network), testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port), + testAccCheckNetworkingV2PortCountFixedIPs(&port, 3), ), }, }, @@ -124,6 +149,25 @@ func TestAccNetworkingV2Port_timeout(t *testing.T) { }) } +func TestAccNetworkingV2Port_fixedIPs(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2PortDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNetworkingV2Port_fixedIPs, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "openstack_networking_port_v2.port_1", "all_fixed_ips.0", "192.168.199.23"), + resource.TestCheckResourceAttr( + "openstack_networking_port_v2.port_1", "all_fixed_ips.1", "192.168.199.24"), + ), + }, + }, + }) +} + func testAccCheckNetworkingV2PortDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) networkingClient, err := config.networkingV2Client(OS_REGION_NAME) @@ -177,6 +221,16 @@ func testAccCheckNetworkingV2PortExists(n string, port *ports.Port) resource.Tes } } +func testAccCheckNetworkingV2PortCountFixedIPs(port *ports.Port, expected int) resource.TestCheckFunc { + return func(s *terraform.State) error { + if len(port.FixedIPs) != expected { + return fmt.Errorf("Expected %d Fixed IPs, got %d", expected, len(port.FixedIPs)) + } + + return nil + } +} + const testAccNetworkingV2Port_basic = ` resource "openstack_networking_network_v2" "network_1" { name = "network_1" @@ -226,6 +280,38 @@ resource "openstack_networking_port_v2" "port_1" { } ` +const testAccNetworkingV2Port_multipleNoIP = ` +resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} + +resource "openstack_networking_subnet_v2" "subnet_1" { + name = "subnet_1" + cidr = "192.168.199.0/24" + ip_version = 4 + network_id = "${openstack_networking_network_v2.network_1.id}" +} + +resource "openstack_networking_port_v2" "port_1" { + name = "port_1" + admin_state_up = "true" + network_id = "${openstack_networking_network_v2.network_1.id}" + + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + } + + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + } + + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + } +} +` + const testAccNetworkingV2Port_allowedAddressPairs = ` resource "openstack_networking_network_v2" "vrrp_network" { name = "vrrp_network" @@ -356,3 +442,33 @@ resource "openstack_networking_port_v2" "port_1" { } } ` + +const testAccNetworkingV2Port_fixedIPs = ` +resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} + +resource "openstack_networking_subnet_v2" "subnet_1" { + name = "subnet_1" + cidr = "192.168.199.0/24" + ip_version = 4 + network_id = "${openstack_networking_network_v2.network_1.id}" +} + +resource "openstack_networking_port_v2" "port_1" { + name = "port_1" + admin_state_up = "true" + network_id = "${openstack_networking_network_v2.network_1.id}" + + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + ip_address = "192.168.199.24" + } + + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + ip_address = "192.168.199.23" + } +} +` diff --git a/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown b/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown index 54a92fb78..9cb62ac40 100644 --- a/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown @@ -95,7 +95,9 @@ The following attributes are exported: * `device_owner` - See Argument Reference above. * `security_group_ids` - See Argument Reference above. * `device_id` - See Argument Reference above. -* `fixed_ip/ip_address` - See Argument Reference above. +* `fixed_ip` - See Argument Reference above. +* `all fixed_ips` - The collection of Fixed IP addresses on the port in the + order returned by the Network v2 API. ## Import