Skip to content

Commit

Permalink
network port extended dhcp opts
Browse files Browse the repository at this point in the history
  • Loading branch information
anton-sidelnikov committed Apr 26, 2024
1 parent e3ad2d7 commit b9c311b
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 28 deletions.
55 changes: 27 additions & 28 deletions docs/resources/networking_port_v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,66 +40,65 @@ resource "opentelekomcloud_networking_subnet_v2" "subnet_1" {

The following arguments are supported:

* `name` - (Optional) A unique name for the port. Changing this
* `name` - (Optional, String) A unique name for the port. Changing this
updates the `name` of an existing port.

* `network_id` - (Required) The ID of the network to attach the port to. Changing
* `network_id` - (Required, String, ForceNew) The ID of the network to attach the port to. Changing
this creates a new port.

* `admin_state_up` - (Optional) Administrative up/down status for the port
* `admin_state_up` - (Optional, Bool) Administrative up/down status for the port
(must be "true" or "false" if provided). Changing this updates the
`admin_state_up` of an existing port.

* `mac_address` - (Optional) Specify a specific MAC address for the port. Changing
* `mac_address` - (Optional, String, ForceNew) Specify a specific MAC address for the port. Changing
this creates a new port.

* `tenant_id` - (Optional) The owner of the Port. Required if admin wants
* `tenant_id` - (Optional, String, ForceNew) The owner of the Port. Required if admin wants
to create a port for another tenant. Changing this creates a new port.

* `device_owner` - (Optional) The device owner of the Port. Changing this creates
* `device_owner` - (Optional. String, ForceNew) The device owner of the Port. Changing this creates
a new port.

* `security_group_ids` - (Optional) A list of security group IDs to apply to the
* `security_group_ids` - (Optional, List) A list of security group IDs to apply to the
port. The security groups must be specified by ID and not name (as opposed
to how they are configured with the Compute Instance).

* `no_security_groups` - (Optional) If set to `true`, then no security groups
* `no_security_groups` - (Optional, Bool) If set to `true`, then no security groups
are applied to the port. If set to `false` and no `security_group_ids` are specified,
then the port will yield to the default behavior of the Networking service,
which is to usually apply the `"default"` security group.

* `port_security_enabled` - (Optional) Whether to explicitly enable or disable
* `port_security_enabled` - (Optional, Bool) Whether to explicitly enable or disable
port security on the port. Port Security is usually enabled by default, so
omitting argument will usually result in a value of `true`. Setting this
explicitly to `false` will disable port security. In order to disable port
security, the port must not have any security groups. Valid values are `true`
and `false`.

* `device_id` - (Optional) The ID of the device attached to the port. Changing this
* `device_id` - (Optional, String, ForceNew) The ID of the device attached to the port. Changing this
creates a new port.

* `fixed_ip` - (Optional) An array of desired IPs for this port. The structure is
* `fixed_ip` - (Optional, List) An array of desired IPs for this port. The structure is
described below. A single `fixed_ip` entry is allowed for a port.

* `allowed_address_pairs` - (Optional) An IP/MAC Address pair of additional IP
The `fixed_ip` block supports:
* `subnet_id` - (Required, String) Subnet in which to allocate IP address for
this port.
* `ip_address` - (Optional, String) IP address desired in the subnet for this port. If
you don't specify `ip_address`, an available IP address from the specified
subnet will be allocated to this port.

* `allowed_address_pairs` - (Optional, Map) An IP/MAC Address pair of additional IP
addresses that can be active on this port. The structure is described below.
The `allowed_address_pairs` block supports:
* `ip_address` - (Required, String) The additional IP address.
* `mac_address` - (Optional, String) The additional MAC address.

* `value_specs` - (Optional) Map of additional options.

The `fixed_ip` block supports:

* `subnet_id` - (Required) Subnet in which to allocate IP address for
this port.

* `ip_address` - (Optional) IP address desired in the subnet for this port. If
you don't specify `ip_address`, an available IP address from the specified
subnet will be allocated to this port.

The `allowed_address_pairs` block supports:

* `ip_address` - (Required) The additional IP address.
* `extra_dhcp_option` - (Optional, Map) Specifies the extended DHCP option. This is an extended attribute.
The `extra_dhcp_option` block supports:
* `name` - (Required, String) Specifies the option name.
* `value` - (Required, String) Specifies the option value.

* `mac_address` - (Optional) The additional MAC address.
* `value_specs` - (Optional, Map) Map of additional options.


## Attributes Reference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,40 @@ func TestAccNetworkingV2Port_portSecurity_enabled(t *testing.T) {
})
}

func TestAccNetworkingV2Port_extendedDhcpOpts(t *testing.T) {
var port testPortWithExtensions
resourceName := "opentelekomcloud_networking_port_v2.port_1"
t.Parallel()
qts := subnetQuotas()
quotas.BookMany(t, qts)

resource.Test(t, resource.TestCase{
PreCheck: func() { common.TestAccPreCheck(t) },
ProviderFactories: common.TestAccProviderFactories,
CheckDestroy: testAccCheckNetworkingV2PortDestroy,
Steps: []resource.TestStep{
{
Config: testAccNetworkingV2PortExtendedDHCPOpts,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2PortWithExtensionsExists(resourceName, &port),
resource.TestCheckResourceAttr(resourceName, "extra_dhcp_option.#", "1"),
resource.TestCheckResourceAttr(resourceName, "extra_dhcp_option.0.name", "A"),
resource.TestCheckResourceAttr(resourceName, "extra_dhcp_option.0.value", "MY_VAL"),
),
},
{
Config: testAccNetworkingV2PortExtendedDHCPOptsUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2PortWithExtensionsExists(resourceName, &port),
resource.TestCheckResourceAttr(resourceName, "extra_dhcp_option.#", "1"),
resource.TestCheckResourceAttr(resourceName, "extra_dhcp_option.0.name", "B"),
resource.TestCheckResourceAttr(resourceName, "extra_dhcp_option.0.value", "MY_VAL_UPDATED"),
),
},
},
})
}

func TestAccNetworkingV2Port_noPortSecurityNoSecurityGroups(t *testing.T) {
var port testPortWithExtensions
resourceName := "opentelekomcloud_networking_port_v2.port_1"
Expand Down Expand Up @@ -514,3 +548,53 @@ resource "opentelekomcloud_networking_port_v2" "port_1" {
}
}
`

const testAccNetworkingV2PortExtendedDHCPOpts = `
resource "opentelekomcloud_networking_network_v2" "network_1" {
name = "network_1"
}
resource "opentelekomcloud_networking_subnet_v2" "subnet_1" {
name = "subnet_1"
cidr = "192.168.199.0/24"
ip_version = 4
network_id = opentelekomcloud_networking_network_v2.network_1.id
}
resource "opentelekomcloud_networking_port_v2" "port_1" {
name = "port_1"
network_id = opentelekomcloud_networking_network_v2.network_1.id
fixed_ip {
subnet_id = opentelekomcloud_networking_subnet_v2.subnet_1.id
ip_address = "192.168.199.23"
}
extra_dhcp_option {
name = "A"
value = "MY_VAL"
}
}
`

const testAccNetworkingV2PortExtendedDHCPOptsUpdate = `
resource "opentelekomcloud_networking_network_v2" "network_1" {
name = "network_1"
}
resource "opentelekomcloud_networking_subnet_v2" "subnet_1" {
name = "subnet_1"
cidr = "192.168.199.0/24"
ip_version = 4
network_id = opentelekomcloud_networking_network_v2.network_1.id
}
resource "opentelekomcloud_networking_port_v2" "port_1" {
name = "port_1"
network_id = opentelekomcloud_networking_network_v2.network_1.id
fixed_ip {
subnet_id = opentelekomcloud_networking_subnet_v2.subnet_1.id
ip_address = "192.168.199.23"
}
extra_dhcp_option {
name = "B"
value = "MY_VAL_UPDATED"
}
}
`
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
"github.com/opentelekomcloud/gophertelekomcloud/openstack/networking/v2/extensions/extradhcpopts"
"github.com/opentelekomcloud/gophertelekomcloud/openstack/networking/v2/extensions/portsecurity"
"github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/helper/hashcode"

Expand Down Expand Up @@ -136,6 +137,23 @@ func ResourceNetworkingPortV2() *schema.Resource {
},
},
},
"extra_dhcp_option": {
Type: schema.TypeSet,
Optional: true,
ForceNew: false,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"value": {
Type: schema.TypeString,
Required: true,
},
},
},
},
"value_specs": {
Type: schema.TypeMap,
Optional: true,
Expand Down Expand Up @@ -209,6 +227,14 @@ func resourceNetworkingPortV2Create(ctx context.Context, d *schema.ResourceData,
PortSecurityEnabled: &portSecurityEnabled,
}

dhcpOpts := d.Get("extra_dhcp_option").(*schema.Set)
if dhcpOpts.Len() > 0 {
extendedCreateOpts = extradhcpopts.CreateOptsExt{
CreateOptsBuilder: createOpts,
ExtraDHCPOpts: expandNetworkingPortDHCPOptsV2Create(dhcpOpts),
}
}

log.Printf("[DEBUG] Create Options: %#v", extendedCreateOpts)
p, err := ports.Create(client, extendedCreateOpts).Extract()
if err != nil {
Expand Down Expand Up @@ -269,6 +295,7 @@ func resourceNetworkingPortV2Read(ctx context.Context, d *schema.ResourceData, m
d.Set("device_id", port.DeviceID),
d.Set("port_security_enabled", port.PortSecurityEnabled),
d.Set("region", config.GetRegion(d)),
d.Set("extra_dhcp_option", flattenNetworkingPortDHCPOptsV2(port.ExtraDHCPOptsExt)),
)

// Create a slice of all returned Fixed IPs.
Expand Down Expand Up @@ -381,6 +408,24 @@ func resourceNetworkingPortV2Update(ctx context.Context, d *schema.ResourceData,
}
}

// Next, perform any dhcp option changes.
if d.HasChange("extra_dhcp_option") {
hasChange = true

o, n := d.GetChange("extra_dhcp_option")
oldDHCPOpts := o.(*schema.Set)
newDHCPOpts := n.(*schema.Set)

deleteDHCPOpts := oldDHCPOpts.Difference(newDHCPOpts)
addDHCPOpts := newDHCPOpts.Difference(oldDHCPOpts)

updateExtraDHCPOpts := expandNetworkingPortDHCPOptsV2Update(deleteDHCPOpts, addDHCPOpts)
finalUpdateOpts = extradhcpopts.UpdateOptsExt{
UpdateOptsBuilder: finalUpdateOpts,
ExtraDHCPOpts: updateExtraDHCPOpts,
}
}

if hasChange {
log.Printf("[DEBUG] Updating Port %s with options: %+v", d.Id(), updateOpts)

Expand Down Expand Up @@ -526,4 +571,76 @@ func waitForNetworkPortDelete(client *golangsdk.ServiceClient, portID string) re
type portWithPortSecurityExtensions struct {
ports.Port
portsecurity.PortSecurityExt
extradhcpopts.ExtraDHCPOptsExt
}

func expandNetworkingPortDHCPOptsV2Create(dhcpOpts *schema.Set) []extradhcpopts.CreateExtraDHCPOpt {
var extraDHCPOpts []extradhcpopts.CreateExtraDHCPOpt

if dhcpOpts != nil {
for _, raw := range dhcpOpts.List() {
rawMap := raw.(map[string]interface{})
optName := rawMap["name"].(string)
optValue := rawMap["value"].(string)

extraDHCPOpts = append(extraDHCPOpts, extradhcpopts.CreateExtraDHCPOpt{
OptName: optName,
OptValue: optValue,
})
}
}

return extraDHCPOpts
}

func flattenNetworkingPortDHCPOptsV2(dhcpOpts extradhcpopts.ExtraDHCPOptsExt) []map[string]interface{} {
dhcpOptsSet := make([]map[string]interface{}, len(dhcpOpts.ExtraDHCPOpts))

for i, dhcpOpt := range dhcpOpts.ExtraDHCPOpts {
dhcpOptsSet[i] = map[string]interface{}{
"name": dhcpOpt.OptName,
"value": dhcpOpt.OptValue,
}
}

return dhcpOptsSet
}

func expandNetworkingPortDHCPOptsV2Update(oldDHCPopts, newDHCPopts *schema.Set) []extradhcpopts.UpdateExtraDHCPOpt {
var extraDHCPOpts []extradhcpopts.UpdateExtraDHCPOpt
var newOptNames []string

if newDHCPopts != nil {
for _, raw := range newDHCPopts.List() {
rawMap := raw.(map[string]interface{})

optName := rawMap["name"].(string)
optValue := rawMap["value"].(string)
// DHCP option name is the primary key, we will check this key below
newOptNames = append(newOptNames, optName)

extraDHCPOpts = append(extraDHCPOpts, extradhcpopts.UpdateExtraDHCPOpt{
OptName: optName,
OptValue: &optValue,
})
}
}

if oldDHCPopts != nil {
for _, raw := range oldDHCPopts.List() {
rawMap := raw.(map[string]interface{})

optName := rawMap["name"].(string)

// if we already add a new option with the same name, it means that we update it, no need to delete
if !common.StrSliceContains(newOptNames, optName) {
extraDHCPOpts = append(extraDHCPOpts, extradhcpopts.UpdateExtraDHCPOpt{
OptName: optName,
OptValue: nil,
})
}
}
}

return extraDHCPOpts
}

0 comments on commit b9c311b

Please sign in to comment.