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:
parent
9848d14bc9
commit
77a41ca859
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue