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.
This commit is contained in:
Joe Topjian 2017-03-27 11:22:56 -06:00 committed by Paul Stack
parent 9848d14bc9
commit 77a41ca859
3 changed files with 132 additions and 12 deletions

View File

@ -85,10 +85,9 @@ func resourceNetworkingPortV2() *schema.Resource {
Computed: true, Computed: true,
}, },
"fixed_ip": &schema.Schema{ "fixed_ip": &schema.Schema{
Type: schema.TypeSet, Type: schema.TypeList,
Optional: true, Optional: true,
ForceNew: false, ForceNew: false,
Computed: true,
Elem: &schema.Resource{ Elem: &schema.Resource{
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
"subnet_id": &schema.Schema{ "subnet_id": &schema.Schema{
@ -98,7 +97,6 @@ func resourceNetworkingPortV2() *schema.Resource {
"ip_address": &schema.Schema{ "ip_address": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Computed: true,
}, },
}, },
}, },
@ -128,6 +126,11 @@ func resourceNetworkingPortV2() *schema.Resource {
Optional: true, Optional: true,
ForceNew: 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("security_group_ids", p.SecurityGroups)
d.Set("device_id", p.DeviceID) d.Set("device_id", p.DeviceID)
// Convert FixedIPs to list of map // Create a slice of all returned Fixed IPs.
var ips []map[string]interface{} // This will be in the order returned by the API,
// which is usually alpha-numeric.
var ips []string
for _, ipObject := range p.FixedIPs { for _, ipObject := range p.FixedIPs {
ip := make(map[string]interface{}) ips = append(ips, ipObject.IPAddress)
ip["subnet_id"] = ipObject.SubnetID
ip["ip_address"] = ipObject.IPAddress
ips = append(ips, ip)
} }
d.Set("fixed_ip", ips) d.Set("all_fixed_ips", ips)
// Convert AllowedAddressPairs to list of map // Convert AllowedAddressPairs to list of map
var pairs []map[string]interface{} var pairs []map[string]interface{}
@ -309,7 +311,7 @@ func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string {
} }
func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} { func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} {
rawIP := d.Get("fixed_ip").(*schema.Set).List() rawIP := d.Get("fixed_ip").([]interface{})
if len(rawIP) == 0 { if len(rawIP) == 0 {
return nil return nil

View File

@ -50,6 +50,30 @@ func TestAccNetworkingV2Port_noip(t *testing.T) {
testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet), testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet),
testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network), testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network),
testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port), 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), testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet),
testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network), testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network),
testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port), 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 { func testAccCheckNetworkingV2PortDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME) 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 = ` const testAccNetworkingV2Port_basic = `
resource "openstack_networking_network_v2" "network_1" { resource "openstack_networking_network_v2" "network_1" {
name = "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 = ` const testAccNetworkingV2Port_allowedAddressPairs = `
resource "openstack_networking_network_v2" "vrrp_network" { resource "openstack_networking_network_v2" "vrrp_network" {
name = "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"
}
}
`

View File

@ -95,7 +95,9 @@ The following attributes are exported:
* `device_owner` - See Argument Reference above. * `device_owner` - See Argument Reference above.
* `security_group_ids` - See Argument Reference above. * `security_group_ids` - See Argument Reference above.
* `device_id` - 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 ## Import