4.12.2
4.12.2
This commit is contained in:
22
internal/service/sdn/logicalports/address_hash.go
Normal file
22
internal/service/sdn/logicalports/address_hash.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package logicalports
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
)
|
||||
|
||||
func hashLogicalPortAddress(v interface{}) int {
|
||||
m := v.(map[string]interface{})
|
||||
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "ip:%s;", m["ip"].(string))
|
||||
fmt.Fprintf(&buf, "ip_type:%s;", m["ip_type"].(string))
|
||||
fmt.Fprintf(&buf, "is_primary:%t;", m["is_primary"].(bool))
|
||||
fmt.Fprintf(&buf, "is_discovered:%t;", m["is_discovered"].(bool))
|
||||
fmt.Fprintf(&buf, "mac:%s;", m["mac"].(string))
|
||||
|
||||
hs := fnv.New32a()
|
||||
hs.Write(buf.Bytes())
|
||||
return int(hs.Sum32())
|
||||
}
|
||||
@@ -24,6 +24,20 @@ func flattenLogicalPortResource(d *schema.ResourceData, logicalPort *logicalport
|
||||
d.Set("bindings", flattenBindings(logicalPort.Bindings))
|
||||
d.Set("labels", flattenLabels(logicalPort.Labels))
|
||||
d.Set("created_at", logicalPort.CreatedAt)
|
||||
d.Set("updated_at", logicalPort.UpdatedAt)
|
||||
d.Set("exclude_firewall", flattenExcludeFirewall(logicalPort.ExcludeFirewall))
|
||||
d.Set("logical_port_addresses", flattenLogicalPortAddresses(logicalPort.Bindings.LogicalPortAddresses))
|
||||
}
|
||||
|
||||
func flattenExcludeFirewall(excludeFirewall logicalports.ExcludeFirewall) []map[string]interface{} {
|
||||
res := make([]map[string]interface{}, 0)
|
||||
temp := map[string]interface{}{
|
||||
"exclusion_reason": excludeFirewall.ExclusionReason,
|
||||
"logical_port_addresses_excluded": excludeFirewall.LogicalPortAddressesExcluded,
|
||||
"logical_port_excluded": excludeFirewall.LogicalPortExcluded,
|
||||
}
|
||||
res = append(res, temp)
|
||||
return res
|
||||
}
|
||||
|
||||
func flattenLogicalPortList(lpl *logicalports.LogicalPortsList) []map[string]interface{} {
|
||||
@@ -79,7 +93,7 @@ func flattenLogicalPort(d *schema.ResourceData, logicalPort *logicalports.Logica
|
||||
|
||||
func flattenLabels(labels logicalports.Labels) []map[string]interface{} {
|
||||
if labels.VMID == "" && labels.VMName == "" {
|
||||
return []map[string]interface{}{}
|
||||
return nil
|
||||
}
|
||||
return []map[string]interface{}{
|
||||
{
|
||||
@@ -106,19 +120,23 @@ func flattenBindings(bindings logicalports.Bindings) []map[string]interface{} {
|
||||
return res
|
||||
}
|
||||
|
||||
func flattenLogicalPortAddress(a logicalports.LogicalPortAddress) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"ip": a.IP,
|
||||
"ip_type": a.IPType,
|
||||
"mac": a.MAC,
|
||||
"id": a.ID,
|
||||
"logical_port_id": a.LogicalPortID,
|
||||
"assigned_at": a.AssignedAt,
|
||||
"is_discovered": a.IsDiscovered,
|
||||
"is_primary": a.IsPrimary,
|
||||
}
|
||||
}
|
||||
|
||||
func flattenLogicalPortAddresses(addrs []logicalports.LogicalPortAddress) []map[string]interface{} {
|
||||
res := make([]map[string]interface{}, 0, len(addrs))
|
||||
for _, a := range addrs {
|
||||
res = append(res, map[string]interface{}{
|
||||
"ip": a.IP,
|
||||
"ip_type": a.IPType,
|
||||
"mac": a.MAC,
|
||||
"id": a.ID,
|
||||
"logical_port_id": a.LogicalPortID,
|
||||
"assigned_at": a.AssignedAt,
|
||||
"is_discovered": a.IsDiscovered,
|
||||
"is_primary": a.IsPrimary,
|
||||
})
|
||||
res = append(res, flattenLogicalPortAddress(a))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
259
internal/service/sdn/logicalports/old_schemas.go
Normal file
259
internal/service/sdn/logicalports/old_schemas.go
Normal file
@@ -0,0 +1,259 @@
|
||||
package logicalports
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||
)
|
||||
|
||||
func resourceLogicalPortResourceV1() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"access_group_id": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Access Group ID",
|
||||
},
|
||||
"description": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Description",
|
||||
},
|
||||
"display_name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Display Name",
|
||||
},
|
||||
"enabled": {
|
||||
Type: schema.TypeBool,
|
||||
Required: true,
|
||||
Description: "Whether the logical port should be enabled",
|
||||
},
|
||||
"hypervisor": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Hypervisor",
|
||||
},
|
||||
"port_security": {
|
||||
Type: schema.TypeBool,
|
||||
Required: true,
|
||||
Description: "Whether the port security is enabled",
|
||||
},
|
||||
"segment_id": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Segment ID",
|
||||
},
|
||||
"adapter_mac": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "Adapter MAC address",
|
||||
},
|
||||
"unique_identifier": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "Unique identifier of the logical port",
|
||||
},
|
||||
"force": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
"migrate": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
Description: "If true, triggers live migration to the hypervisor specified in the 'hypervisor' field. If false, hypervisor changes are applied via the regular update endpoint.",
|
||||
},
|
||||
"logical_port_addresses": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"ip": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "IP address of the logical port",
|
||||
},
|
||||
"ip_type": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringInSlice([]string{"IPv4", "IPv6"}, false),
|
||||
},
|
||||
"is_primary": {
|
||||
Type: schema.TypeBool,
|
||||
Required: true,
|
||||
},
|
||||
"is_discovered": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
"mac": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "ID of the logical port",
|
||||
},
|
||||
"access_group_name": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Name of the access group",
|
||||
},
|
||||
"address_detection": {
|
||||
Type: schema.TypeBool,
|
||||
Computed: true,
|
||||
},
|
||||
"created_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"external_network_id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"hypervisor_display_name": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"live_migration_target_hv": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"status": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"operation_status": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Operation status",
|
||||
},
|
||||
"hypervisor_status": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Hypervisor status",
|
||||
},
|
||||
"hypervisors": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"operation_status": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Operation status of the hypervisor",
|
||||
},
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Name of the hypervisor",
|
||||
},
|
||||
"display_name": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Display name of the hypervisor",
|
||||
},
|
||||
"hypervisor_status": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Status of the hypervisor",
|
||||
},
|
||||
"synced_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Sync time of the hypervisor",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"bindings": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Unique identifier of the binding",
|
||||
},
|
||||
"segment_id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Unique identifier of the segment",
|
||||
},
|
||||
"segment_display_name": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Display name of the segment",
|
||||
},
|
||||
"port_security": {
|
||||
Type: schema.TypeBool,
|
||||
Computed: true,
|
||||
Description: "If the port is secured",
|
||||
},
|
||||
"address_detection": {
|
||||
Type: schema.TypeBool,
|
||||
Computed: true,
|
||||
Description: "If the adapter address detection is enabled",
|
||||
},
|
||||
"version_id": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Description: "Version ID of the binding",
|
||||
},
|
||||
"created_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Creation time of the binding",
|
||||
},
|
||||
"updated_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Update time of the binding",
|
||||
},
|
||||
"logical_port_addresses": logicalPortAddressesSchema(),
|
||||
},
|
||||
},
|
||||
},
|
||||
"version_id": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Description: "Version ID of the logical port",
|
||||
},
|
||||
"labels": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"vm_id": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "VM ID label",
|
||||
},
|
||||
"vm_name": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "VM name label",
|
||||
},
|
||||
},
|
||||
},
|
||||
Description: "Labels",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package logicalports
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
@@ -16,6 +18,8 @@ func resourceLogicalPortCreate(ctx context.Context, d *schema.ResourceData, m in
|
||||
d.Get("display_name").(string))
|
||||
c := m.(*controller.ControllerCfg)
|
||||
|
||||
var diags diag.Diagnostics
|
||||
|
||||
req := logicalports.CreateRequest{
|
||||
AccessGroupID: d.Get("access_group_id").(string),
|
||||
Description: d.Get("description").(string),
|
||||
@@ -36,42 +40,74 @@ func resourceLogicalPortCreate(ctx context.Context, d *schema.ResourceData, m in
|
||||
labelsList := labelsRaw.([]interface{})
|
||||
if len(labelsList) > 0 {
|
||||
labelsMap := labelsList[0].(map[string]interface{})
|
||||
req.Labels = logicalports.CreateLabels{
|
||||
req.Labels = &logicalports.CreateLabels{
|
||||
VMID: labelsMap["vm_id"].(string),
|
||||
VMName: labelsMap["vm_name"].(string),
|
||||
}
|
||||
}
|
||||
}
|
||||
if logicalPortAddresses, ok := d.GetOk("logical_port_addresses"); ok {
|
||||
logicalPortAddressesList := logicalPortAddresses.([]interface{})
|
||||
logicalPortsAddressesArr := make([]logicalports.LogicalPortAddress, 0)
|
||||
logicalPortAddressesList := logicalPortAddresses.(*schema.Set).List()
|
||||
logicalPortsAddressesArr := make([]logicalports.LogicalPortAddressRequest, 0)
|
||||
primaryCount := 0
|
||||
ipMap := make(map[string]bool)
|
||||
blankMACIPs := make([]string, 0)
|
||||
|
||||
for _, logicalPortAddressRaw := range logicalPortAddressesList {
|
||||
logicalPortAddressMap := logicalPortAddressRaw.(map[string]interface{})
|
||||
logicalPortAddress := logicalports.LogicalPortAddress{
|
||||
IP: logicalPortAddressMap["ip"].(string),
|
||||
IPType: logicalPortAddressMap["ip_type"].(string),
|
||||
IsPrimary: logicalPortAddressMap["is_primary"].(bool),
|
||||
ip := logicalPortAddressMap["ip"].(string)
|
||||
isPrimary := logicalPortAddressMap["is_primary"].(bool)
|
||||
|
||||
if isPrimary {
|
||||
primaryCount++
|
||||
if primaryCount > 1 {
|
||||
return diag.Errorf("logical_port_addresses: only one address can be primary (is_primary = true)")
|
||||
}
|
||||
}
|
||||
if isDiscovered, ok := logicalPortAddressMap["is_discovered"]; ok {
|
||||
logicalPortAddress.IsDiscovered = isDiscovered.(bool)
|
||||
|
||||
if ipMap[ip] {
|
||||
return diag.Errorf("logical_port_addresses: duplicate IP address %s is not allowed", ip)
|
||||
}
|
||||
if mac, ok := logicalPortAddressMap["mac"]; ok {
|
||||
logicalPortAddress.MAC = mac.(string)
|
||||
ipMap[ip] = true
|
||||
|
||||
mac := logicalPortAddressMap["mac"].(string)
|
||||
if mac == "" {
|
||||
blankMACIPs = append(blankMACIPs, ip)
|
||||
}
|
||||
logicalPortsAddressesArr = append(logicalPortsAddressesArr, logicalPortAddress)
|
||||
|
||||
logicalPortsAddressesArr = append(logicalPortsAddressesArr, logicalports.LogicalPortAddressRequest{
|
||||
IP: ip,
|
||||
IPType: logicalPortAddressMap["ip_type"].(string),
|
||||
IsPrimary: isPrimary,
|
||||
IsDiscovered: logicalPortAddressMap["is_discovered"].(bool),
|
||||
MAC: mac,
|
||||
})
|
||||
}
|
||||
|
||||
if len(blankMACIPs) > 0 {
|
||||
diags = append(diags, diag.Diagnostic{
|
||||
Severity: diag.Warning,
|
||||
Summary: "logical_port_addresses: mac not set, will be auto-generated",
|
||||
Detail: fmt.Sprintf(
|
||||
"MAC is not set for the following addresses, the platform will auto-generate it for each: %s. "+
|
||||
"Every subsequent \"terraform plan\" will show these addresses as changed (a harmless no-op diff) until mac is set explicitly.",
|
||||
strings.Join(blankMACIPs, ", "),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
req.LogicalPortAddresses = logicalPortsAddressesArr
|
||||
}
|
||||
|
||||
logicalPort, err := c.SDN().LogicalPorts().Create(ctx, req)
|
||||
if err != nil {
|
||||
d.SetId("")
|
||||
return diag.FromErr(err)
|
||||
return append(diags, diag.FromErr(err)...)
|
||||
}
|
||||
|
||||
d.SetId(logicalPort.ID)
|
||||
|
||||
return resourceLogicalPortRead(ctx, d, m)
|
||||
return append(diags, resourceLogicalPortRead(ctx, d, m)...)
|
||||
}
|
||||
|
||||
func resourceLogicalPortRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||||
@@ -95,6 +131,12 @@ func resourceLogicalPortUpdate(ctx context.Context, d *schema.ResourceData, m in
|
||||
|
||||
migrate := d.Get("migrate").(bool)
|
||||
|
||||
if _, ok := d.GetOk("adapter_mac"); !ok {
|
||||
return diag.Errorf("the adapter_mac field is required when updating")
|
||||
}
|
||||
|
||||
var diags diag.Diagnostics
|
||||
|
||||
req := logicalports.UpdateRequest{
|
||||
LogicalPortID: d.Id(),
|
||||
VersionID: uint64(d.Get("version_id").(int)),
|
||||
@@ -106,132 +148,167 @@ func resourceLogicalPortUpdate(ctx context.Context, d *schema.ResourceData, m in
|
||||
SegmentID: d.Get("segment_id").(string),
|
||||
}
|
||||
|
||||
if !migrate {
|
||||
req.Hypervisor = d.Get("hypervisor").(string)
|
||||
} else {
|
||||
hvChanged := d.HasChange("hypervisor")
|
||||
if migrate && hvChanged {
|
||||
old, _ := d.GetChange("hypervisor")
|
||||
req.Hypervisor = old.(string)
|
||||
} else {
|
||||
req.Hypervisor = d.Get("hypervisor").(string)
|
||||
if hvChanged {
|
||||
diags = append(diags, diag.Diagnostic{
|
||||
Severity: diag.Warning,
|
||||
Summary: "hypervisor changed without migrate=true",
|
||||
Detail: "hypervisor was changed but flag \"migrate\" is false, this change is being sent via the regular " +
|
||||
"update endpoint instead of a live migration. If you intended to live migrate this logical port, " +
|
||||
"set migrate = true.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if d.HasChange("labels") {
|
||||
if labelsRaw, ok := d.GetOk("labels"); ok {
|
||||
labelsList := labelsRaw.([]interface{})
|
||||
if len(labelsList) > 0 {
|
||||
labelsMap := labelsList[0].(map[string]interface{})
|
||||
req.Labels = logicalports.UpdateLabels{
|
||||
VMID: labelsMap["vm_id"].(string),
|
||||
VMName: labelsMap["vm_name"].(string),
|
||||
}
|
||||
if labelsRaw, ok := d.GetOk("labels"); ok {
|
||||
labelsList := labelsRaw.([]interface{})
|
||||
if len(labelsList) > 0 {
|
||||
labelsMap := labelsList[0].(map[string]interface{})
|
||||
req.Labels = &logicalports.UpdateLabels{
|
||||
VMID: labelsMap["vm_id"].(string),
|
||||
VMName: labelsMap["vm_name"].(string),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if d.HasChange("logical_port_addresses") {
|
||||
oldAddresses, newAddresses := d.GetChange("logical_port_addresses")
|
||||
oldAddressesList := oldAddresses.([]interface{})
|
||||
newAddressesList := newAddresses.([]interface{})
|
||||
oldAddressesMap := make(map[string]logicalports.LogicalPortAddress)
|
||||
|
||||
oldAddressesList := oldAddresses.(*schema.Set).List()
|
||||
oldAddressesMap := make(map[string]logicalports.LogicalPortAddress, len(oldAddressesList))
|
||||
for _, oldAddressRaw := range oldAddressesList {
|
||||
logicalPortAddressMap := oldAddressRaw.(map[string]interface{})
|
||||
logicalPortAddress := logicalports.LogicalPortAddress{
|
||||
IP: logicalPortAddressMap["ip"].(string),
|
||||
IPType: logicalPortAddressMap["ip_type"].(string),
|
||||
IsPrimary: logicalPortAddressMap["is_primary"].(bool),
|
||||
}
|
||||
if isDiscovered, ok := logicalPortAddressMap["is_discovered"]; ok {
|
||||
logicalPortAddress.IsDiscovered = isDiscovered.(bool)
|
||||
}
|
||||
if mac, ok := logicalPortAddressMap["mac"]; ok {
|
||||
logicalPortAddress.MAC = mac.(string)
|
||||
ID: logicalPortAddressMap["id"].(string),
|
||||
IP: logicalPortAddressMap["ip"].(string),
|
||||
IPType: logicalPortAddressMap["ip_type"].(string),
|
||||
IsPrimary: logicalPortAddressMap["is_primary"].(bool),
|
||||
IsDiscovered: logicalPortAddressMap["is_discovered"].(bool),
|
||||
MAC: logicalPortAddressMap["mac"].(string),
|
||||
}
|
||||
oldAddressesMap[logicalPortAddress.IP] = logicalPortAddress
|
||||
}
|
||||
newAddressesMap := make(map[string]logicalports.LogicalPortAddress)
|
||||
|
||||
newAddressesList := newAddresses.(*schema.Set).List()
|
||||
newAddressesMap := make(map[string]logicalports.LogicalPortAddress, len(newAddressesList))
|
||||
primaryCount := 0
|
||||
for _, newAddressRaw := range newAddressesList {
|
||||
logicalPortAddressMap := newAddressRaw.(map[string]interface{})
|
||||
logicalPortAddress := logicalports.LogicalPortAddress{
|
||||
IP: logicalPortAddressMap["ip"].(string),
|
||||
IPType: logicalPortAddressMap["ip_type"].(string),
|
||||
IsPrimary: logicalPortAddressMap["is_primary"].(bool),
|
||||
ID: logicalPortAddressMap["id"].(string),
|
||||
IP: logicalPortAddressMap["ip"].(string),
|
||||
IPType: logicalPortAddressMap["ip_type"].(string),
|
||||
IsPrimary: logicalPortAddressMap["is_primary"].(bool),
|
||||
IsDiscovered: logicalPortAddressMap["is_discovered"].(bool),
|
||||
MAC: logicalPortAddressMap["mac"].(string),
|
||||
}
|
||||
if isDiscovered, ok := logicalPortAddressMap["is_discovered"]; ok {
|
||||
logicalPortAddress.IsDiscovered = isDiscovered.(bool)
|
||||
if logicalPortAddress.IsPrimary {
|
||||
primaryCount++
|
||||
if primaryCount > 1 {
|
||||
return append(diags, diag.Errorf("logical_port_addresses: only one address can be primary (is_primary = true)")...)
|
||||
}
|
||||
}
|
||||
if mac, ok := logicalPortAddressMap["mac"]; ok {
|
||||
logicalPortAddress.MAC = mac.(string)
|
||||
if logicalPortAddress.IP != "" {
|
||||
if bar, ok := newAddressesMap[logicalPortAddress.IP]; ok {
|
||||
return append(diags, diag.Errorf("it is impossible to create ports with the same IP %s", bar.IP)...)
|
||||
}
|
||||
}
|
||||
newAddressesMap[logicalPortAddress.IP] = logicalPortAddress
|
||||
}
|
||||
|
||||
removeAddresses := make([]string, 0)
|
||||
for addressIP := range oldAddressesMap {
|
||||
for addressIP, lp := range oldAddressesMap {
|
||||
if _, exists := newAddressesMap[addressIP]; !exists {
|
||||
removeAddresses = append(removeAddresses, addressIP)
|
||||
removeAddresses = append(removeAddresses, lp.ID)
|
||||
}
|
||||
}
|
||||
req.RemoveAddresses = removeAddresses
|
||||
|
||||
for addressIP, address := range newAddressesMap {
|
||||
if oldAddressIP, exists := oldAddressesMap[addressIP]; !exists || address != oldAddressIP {
|
||||
if !exists {
|
||||
logicalPortAddress := logicalports.AddAddress{
|
||||
IP: address.IP,
|
||||
IPType: address.IPType,
|
||||
IsPrimary: address.IsPrimary,
|
||||
IsDiscovered: address.IsDiscovered,
|
||||
MAC: address.MAC,
|
||||
}
|
||||
req.AddAddresses = append(req.AddAddresses, logicalPortAddress)
|
||||
} else if address != oldAddressIP {
|
||||
logicalPortAddress := logicalports.UpdateAddress{
|
||||
IP: address.IP,
|
||||
IPType: address.IPType,
|
||||
IsPrimary: address.IsPrimary,
|
||||
IsDiscovered: address.IsDiscovered,
|
||||
MAC: address.MAC,
|
||||
}
|
||||
req.UpdateAddresses = append(req.UpdateAddresses, logicalPortAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
oldAddress, exists := oldAddressesMap[addressIP]
|
||||
|
||||
// mac field fallback
|
||||
currMac := address.MAC
|
||||
if exists && currMac == "" {
|
||||
currMac = oldAddress.MAC
|
||||
}
|
||||
|
||||
if !exists {
|
||||
if currMac == "" {
|
||||
return append(diags, diag.Errorf("logical_port_addresses: mac is required when adding a new address to an existing logical port (ip=%s)", address.IP)...)
|
||||
}
|
||||
req.AddAddresses = append(req.AddAddresses, logicalports.AddAddress{
|
||||
IP: address.IP,
|
||||
IPType: address.IPType,
|
||||
IsPrimary: address.IsPrimary,
|
||||
IsDiscovered: address.IsDiscovered,
|
||||
MAC: currMac,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// compare without compute fields: id, logical_port_id, assigned_at
|
||||
changed := currMac != oldAddress.MAC ||
|
||||
address.IPType != oldAddress.IPType ||
|
||||
address.IsPrimary != oldAddress.IsPrimary ||
|
||||
address.IsDiscovered != oldAddress.IsDiscovered
|
||||
if !changed {
|
||||
continue
|
||||
}
|
||||
|
||||
if oldAddress.ID == "" {
|
||||
return append(diags, diag.Errorf("logical_port_addresses: internal error, missing platform id for existing address (ip=%s)", address.IP)...)
|
||||
}
|
||||
req.UpdateAddresses = append(req.UpdateAddresses, logicalports.UpdateAddress{
|
||||
ID: oldAddress.ID,
|
||||
IP: address.IP,
|
||||
IPType: address.IPType,
|
||||
IsPrimary: address.IsPrimary,
|
||||
IsDiscovered: address.IsDiscovered,
|
||||
MAC: currMac,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_, err := c.SDN().LogicalPorts().Update(ctx, req)
|
||||
if err != nil {
|
||||
d.SetId("")
|
||||
return diag.FromErr(err)
|
||||
return append(diags, diag.FromErr(err)...)
|
||||
}
|
||||
|
||||
if migrate {
|
||||
if targetHV, ok := d.GetOk("hypervisor"); ok {
|
||||
// Re-read version_id
|
||||
lp, err := utilityLogicalPortCheckPresence(ctx, d, m)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
if migrate && hvChanged {
|
||||
targetHV := d.Get("hypervisor").(string)
|
||||
|
||||
migrateReq := logicalports.MigrateStartRequest{
|
||||
LogicalPortID: d.Id(),
|
||||
VersionID: lp.VersionID,
|
||||
TargetHypervisor: targetHV.(string),
|
||||
}
|
||||
|
||||
_, err = c.SDN().LogicalPorts().StartMigrate(ctx, migrateReq)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
// to prevent drift on the next plan
|
||||
diags := resourceLogicalPortRead(ctx, d, m)
|
||||
if diags.HasError() {
|
||||
return diags
|
||||
}
|
||||
d.Set("hypervisor", targetHV.(string))
|
||||
return diags
|
||||
// re-read version_id
|
||||
lp, err := utilityLogicalPortCheckPresence(ctx, d, m)
|
||||
if err != nil {
|
||||
return append(diags, diag.FromErr(err)...)
|
||||
}
|
||||
|
||||
migrateReq := logicalports.MigrateStartRequest{
|
||||
LogicalPortID: d.Id(),
|
||||
VersionID: lp.VersionID,
|
||||
TargetHypervisor: targetHV,
|
||||
}
|
||||
|
||||
_, err = c.SDN().LogicalPorts().StartMigrate(ctx, migrateReq)
|
||||
if err != nil {
|
||||
return append(diags, diag.FromErr(err)...)
|
||||
}
|
||||
// to prevent drift on the next plan
|
||||
readDiags := resourceLogicalPortRead(ctx, d, m)
|
||||
if readDiags.HasError() {
|
||||
return append(diags, readDiags...)
|
||||
}
|
||||
d.Set("hypervisor", targetHV)
|
||||
return append(diags, readDiags...)
|
||||
}
|
||||
|
||||
return resourceLogicalPortRead(ctx, d, m)
|
||||
return append(diags, resourceLogicalPortRead(ctx, d, m)...)
|
||||
}
|
||||
|
||||
func resourceLogicalPortDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||||
@@ -259,7 +336,7 @@ func resourceLogicalPortDelete(ctx context.Context, d *schema.ResourceData, m in
|
||||
|
||||
func ResourceLogicalPort() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
SchemaVersion: 1,
|
||||
SchemaVersion: 2,
|
||||
|
||||
CreateContext: resourceLogicalPortCreate,
|
||||
ReadContext: resourceLogicalPortRead,
|
||||
@@ -279,5 +356,12 @@ func ResourceLogicalPort() *schema.Resource {
|
||||
},
|
||||
|
||||
Schema: resourceLogicalPortSchemaMake(),
|
||||
StateUpgraders: []schema.StateUpgrader{
|
||||
{
|
||||
Type: resourceLogicalPortResourceV1().CoreConfigSchema().ImpliedType(),
|
||||
Upgrade: resourceLogicalPortUpgradeV1,
|
||||
Version: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,12 +48,38 @@ func logicalPortAddressesSchema() *schema.Schema {
|
||||
}
|
||||
}
|
||||
|
||||
func excludeFirewallSchema() *schema.Schema {
|
||||
return &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"exclusion_reason": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Reason for the firewall exclusion",
|
||||
},
|
||||
"logical_port_addresses_excluded": {
|
||||
Type: schema.TypeBool,
|
||||
Computed: true,
|
||||
Description: "Whether logical port addresses are excluded from the firewall",
|
||||
},
|
||||
"logical_port_excluded": {
|
||||
Type: schema.TypeBool,
|
||||
Computed: true,
|
||||
Description: "Whether the logical port is excluded from the firewall",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceLogicalPortSchemaMake() map[string]*schema.Schema {
|
||||
res := map[string]*schema.Schema{
|
||||
"access_group_id": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Access Group ID",
|
||||
Description: "UUID of the access group",
|
||||
},
|
||||
"description": {
|
||||
Type: schema.TypeString,
|
||||
@@ -73,7 +99,7 @@ func resourceLogicalPortSchemaMake() map[string]*schema.Schema {
|
||||
"hypervisor": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Hypervisor",
|
||||
Description: "Associated hypervisor",
|
||||
},
|
||||
"port_security": {
|
||||
Type: schema.TypeBool,
|
||||
@@ -83,7 +109,7 @@ func resourceLogicalPortSchemaMake() map[string]*schema.Schema {
|
||||
"segment_id": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Segment ID",
|
||||
Description: "UUID of the network segment",
|
||||
},
|
||||
"adapter_mac": {
|
||||
Type: schema.TypeString,
|
||||
@@ -94,6 +120,7 @@ func resourceLogicalPortSchemaMake() map[string]*schema.Schema {
|
||||
"unique_identifier": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "Unique identifier of the logical port",
|
||||
},
|
||||
"force": {
|
||||
@@ -105,26 +132,29 @@ func resourceLogicalPortSchemaMake() map[string]*schema.Schema {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
Description: "If true, triggers live migration to the hypervisor specified in the 'hypervisor' field. If false, hypervisor changes are applied via the regular update endpoint.",
|
||||
Description: "If true, triggers live migration to the hypervisor specified in the 'hypervisor' field. If false, hypervisor changes are applied via the regular update endpoint",
|
||||
},
|
||||
"logical_port_addresses": {
|
||||
Type: schema.TypeList,
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Set: hashLogicalPortAddress,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"ip_type": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringInSlice([]string{"IPv4", "IPv6"}, false),
|
||||
Description: "IP type",
|
||||
},
|
||||
"ip": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "IP address of the logical port",
|
||||
},
|
||||
"ip_type": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringInSlice([]string{"IPv4", "IPv6"}, false),
|
||||
},
|
||||
"is_primary": {
|
||||
Type: schema.TypeBool,
|
||||
Required: true,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
"is_discovered": {
|
||||
Type: schema.TypeBool,
|
||||
@@ -132,8 +162,26 @@ func resourceLogicalPortSchemaMake() map[string]*schema.Schema {
|
||||
Default: false,
|
||||
},
|
||||
"mac": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ValidateFunc: validation.IsMACAddress,
|
||||
Description: "MAC address; optional on initial port creation & required when adding or updating addresses in an existing logical port",
|
||||
},
|
||||
"id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "ID of the address, assigned by the platform",
|
||||
},
|
||||
"logical_port_id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "ID of the parent logical port",
|
||||
},
|
||||
"assigned_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Timestamp when the address was assigned",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -156,6 +204,11 @@ func resourceLogicalPortSchemaMake() map[string]*schema.Schema {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"updated_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Last update time of the logical port",
|
||||
},
|
||||
"external_network_id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
@@ -168,6 +221,7 @@ func resourceLogicalPortSchemaMake() map[string]*schema.Schema {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"exclude_firewall": excludeFirewallSchema(),
|
||||
"status": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
|
||||
13
internal/service/sdn/logicalports/state_upgraders.go
Normal file
13
internal/service/sdn/logicalports/state_upgraders.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package logicalports
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// "logical_port_addresses" from TypeList to TypeSet
|
||||
func resourceLogicalPortUpgradeV1(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
|
||||
log.Debug("resourceLogicalPortUpgradeV1: upgrading state")
|
||||
return rawState, nil
|
||||
}
|
||||
Reference in New Issue
Block a user