Files
terraform-provider-unifi/internal/provider/v1/resource_port_profile.go
Mateusz Filipowicz 325d7b7f20 feat: initialize Terraform Plugin Framework (#23)
* feat: initialize Terraform Plugin Framework

* fix docker-compose path for tests

* fix: ensure documentation can be generated with old provider SDK and new plugin framework

* lint
2025-02-24 00:11:41 +01:00

431 lines
16 KiB
Go

package v1
import (
"context"
"errors"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
func resourcePortProfile() *schema.Resource {
return &schema.Resource{
Description: "`unifi_port_profile` manages a port profile for use on network switches.",
CreateContext: resourcePortProfileCreate,
ReadContext: resourcePortProfileRead,
UpdateContext: resourcePortProfileUpdate,
DeleteContext: resourcePortProfileDelete,
Importer: &schema.ResourceImporter{
StateContext: importSiteAndID,
},
Schema: map[string]*schema.Schema{
"id": {
Description: "The ID of the port profile.",
Type: schema.TypeString,
Computed: true,
},
"site": {
Description: "The name of the site to associate the port profile with.",
Type: schema.TypeString,
Computed: true,
Optional: true,
ForceNew: true,
},
"autoneg": {
Description: "Enable link auto negotiation for the port profile. When set to `true` this overrides `speed`.",
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"dot1x_ctrl": {
Description: "The type of 802.1X control to use. Can be `auto`, `force_authorized`, `force_unauthorized`, `mac_based` or `multi_host`.",
Type: schema.TypeString,
Optional: true,
Default: "force_authorized",
ValidateFunc: validation.StringInSlice([]string{"auto", "force_authorized", "force_unauthorized", "mac_based", "multi_host"}, false),
},
"dot1x_idle_timeout": {
Description: "The timeout, in seconds, to use when using the MAC Based 802.1X control. Can be between 0 and 65535",
Type: schema.TypeInt,
Optional: true,
Default: 300,
ValidateFunc: validation.IntBetween(0, 65535),
},
"egress_rate_limit_kbps": {
Description: "The egress rate limit, in kpbs, for the port profile. Can be between `64` and `9999999`.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(64, 9999999),
},
"egress_rate_limit_kbps_enabled": {
Description: "Enable egress rate limiting for the port profile.",
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"excluded_network_ids": {
Description: "List of network IDs to exclude on the port profile when forward is set to customize.",
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"forward": {
Description: "The type forwarding to use for the port profile. Can be `all`, `native`, `customize` or `disabled`.",
Type: schema.TypeString,
Optional: true,
Default: "native",
ValidateFunc: validation.StringInSlice([]string{"all", "native", "customize", "disabled"}, false),
},
"full_duplex": {
Description: "Enable full duplex for the port profile.",
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"isolation": {
Description: "Enable port isolation for the port profile.",
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"lldpmed_enabled": {
Description: "Enable LLDP-MED for the port profile.",
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"lldpmed_notify_enabled": {
Description: "Enable LLDP-MED topology change notifications for the port profile.",
Type: schema.TypeBool,
Optional: true,
//ValidateFunc: ,
},
// TODO: rename to native_network_id
"native_networkconf_id": {
Description: "The ID of network to use as the main network on the port profile.",
Type: schema.TypeString,
Optional: true,
},
"name": {
Description: "The name of the port profile.",
Type: schema.TypeString,
Optional: true,
},
"op_mode": {
Description: "The operation mode for the port profile. Can only be `switch`",
Type: schema.TypeString,
Optional: true,
Default: "switch",
ValidateFunc: validation.StringInSlice([]string{"switch"}, false),
},
"poe_mode": {
Description: "The POE mode for the port profile. Can be one of `auto`, `passv24`, `passthrough` or `off`.",
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"auto", "passv24", "passthrough", "off"}, false),
},
"port_security_enabled": {
Description: "Enable port security for the port profile.",
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"port_security_mac_address": {
Description: "The MAC addresses associated with the port security for the port profile.",
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"priority_queue1_level": {
Description: "The priority queue 1 level for the port profile. Can be between 0 and 100.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100),
},
"priority_queue2_level": {
Description: "The priority queue 2 level for the port profile. Can be between 0 and 100.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100),
},
"priority_queue3_level": {
Description: "The priority queue 3 level for the port profile. Can be between 0 and 100.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100),
},
"priority_queue4_level": {
Description: "The priority queue 4 level for the port profile. Can be between 0 and 100.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100),
},
"speed": {
Description: "The link speed to set for the port profile in Mbps. Can be one of `10`, `100`, `1000`, `2500`, `5000`, `10000`, `20000`, `25000`, `40000`, `50000` or `100000`. When `autoneg` is true, this setting is ignored.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntInSlice([]int{10, 100, 1000, 2500, 5000, 10000, 20000, 25000, 40000, 50000, 100000}),
},
"stormctrl_bcast_enabled": {
Description: "Enable broadcast Storm Control for the port profile.",
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"stormctrl_bcast_level": {
Description: "The broadcast Storm Control level for the port profile. Can be between 0 and 100.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100),
ConflictsWith: []string{"stormctrl_bcast_rate"},
},
"stormctrl_bcast_rate": {
Description: "The broadcast Storm Control rate for the port profile. Can be between 0 and 14880000.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 14880000),
},
"stormctrl_mcast_enabled": {
Description: "Enable multicast Storm Control for the port profile.",
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"stormctrl_mcast_level": {
Description: "The multicast Storm Control level for the port profile. Can be between 0 and 100.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100),
ConflictsWith: []string{"stormctrl_mcast_rate"},
},
"stormctrl_mcast_rate": {
Description: "The multicast Storm Control rate for the port profile. Can be between 0 and 14880000.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 14880000),
},
"stormctrl_type": {
Description: "The type of Storm Control to use for the port profile. Can be one of `level` or `rate`.",
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"level", "rate"}, false),
},
"stormctrl_ucast_enabled": {
Description: "Enable unknown unicast Storm Control for the port profile.",
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"stormctrl_ucast_level": {
Description: "The unknown unicast Storm Control level for the port profile. Can be between 0 and 100.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100),
ConflictsWith: []string{"stormctrl_ucast_rate"},
},
"stormctrl_ucast_rate": {
Description: "The unknown unicast Storm Control rate for the port profile. Can be between 0 and 14880000.",
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 14880000),
},
"stp_port_mode": {
Description: "Enable spanning tree protocol on the port profile.",
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"tagged_vlan_mgmt": {
Description: "The VLAN management type for the port profile. Can be one of 'auto', 'block_all', or 'custom'.",
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"auto", "block_all", "custom"}, false),
},
// TODO: rename to voice_network_id
"voice_networkconf_id": {
Description: "The ID of network to use as the voice network on the port profile.",
Type: schema.TypeString,
Optional: true,
},
},
}
}
func resourcePortProfileCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*provider.Client)
req, err := resourcePortProfileGetResourceData(d)
if err != nil {
return diag.FromErr(err)
}
site := d.Get("site").(string)
if site == "" {
site = c.Site
}
resp, err := c.CreatePortProfile(ctx, site, req)
if err != nil {
return diag.FromErr(err)
}
d.SetId(resp.ID)
return resourcePortProfileSetResourceData(resp, d, site)
}
func resourcePortProfileGetResourceData(d *schema.ResourceData) (*unifi.PortProfile, error) {
portSecurityMacAddress, err := utils.SetToStringSlice(d.Get("port_security_mac_address").(*schema.Set))
if err != nil {
return nil, err
}
excludedNetworkIDs, err := utils.SetToStringSlice(d.Get("excluded_network_ids").(*schema.Set))
if err != nil {
return nil, err
}
return &unifi.PortProfile{
Autoneg: d.Get("autoneg").(bool),
Dot1XCtrl: d.Get("dot1x_ctrl").(string),
Dot1XIDleTimeout: d.Get("dot1x_idle_timeout").(int),
EgressRateLimitKbps: d.Get("egress_rate_limit_kbps").(int),
EgressRateLimitKbpsEnabled: d.Get("egress_rate_limit_kbps_enabled").(bool),
ExcludedNetworkIDs: excludedNetworkIDs,
Forward: d.Get("forward").(string),
FullDuplex: d.Get("full_duplex").(bool),
Isolation: d.Get("isolation").(bool),
LldpmedEnabled: d.Get("lldpmed_enabled").(bool),
LldpmedNotifyEnabled: d.Get("lldpmed_notify_enabled").(bool),
NATiveNetworkID: d.Get("native_networkconf_id").(string),
Name: d.Get("name").(string),
OpMode: d.Get("op_mode").(string),
PoeMode: d.Get("poe_mode").(string),
PortSecurityEnabled: d.Get("port_security_enabled").(bool),
PortSecurityMACAddress: portSecurityMacAddress,
PriorityQueue1Level: d.Get("priority_queue1_level").(int),
PriorityQueue2Level: d.Get("priority_queue2_level").(int),
PriorityQueue3Level: d.Get("priority_queue3_level").(int),
PriorityQueue4Level: d.Get("priority_queue4_level").(int),
Speed: d.Get("speed").(int),
StormctrlBroadcastastEnabled: d.Get("stormctrl_bcast_enabled").(bool),
StormctrlBroadcastastLevel: d.Get("stormctrl_bcast_level").(int),
StormctrlBroadcastastRate: d.Get("stormctrl_bcast_rate").(int),
StormctrlMcastEnabled: d.Get("stormctrl_mcast_enabled").(bool),
StormctrlMcastLevel: d.Get("stormctrl_mcast_level").(int),
StormctrlMcastRate: d.Get("stormctrl_mcast_rate").(int),
StormctrlType: d.Get("stormctrl_type").(string),
StormctrlUcastEnabled: d.Get("stormctrl_ucast_enabled").(bool),
StormctrlUcastLevel: d.Get("stormctrl_ucast_level").(int),
StormctrlUcastRate: d.Get("stormctrl_ucast_rate").(int),
StpPortMode: d.Get("stp_port_mode").(bool),
TaggedVLANMgmt: d.Get("tagged_vlan_mgmt").(string),
VoiceNetworkID: d.Get("voice_networkconf_id").(string),
}, nil
}
func resourcePortProfileSetResourceData(resp *unifi.PortProfile, d *schema.ResourceData, site string) diag.Diagnostics {
d.Set("site", site)
d.Set("autoneg", resp.Autoneg)
d.Set("dot1x_ctrl", resp.Dot1XCtrl)
d.Set("dot1x_idle_timeout", resp.Dot1XIDleTimeout)
d.Set("egress_rate_limit_kbps", resp.EgressRateLimitKbps)
d.Set("egress_rate_limit_kbps_enabled", resp.EgressRateLimitKbpsEnabled)
d.Set("excluded_network_ids", utils.StringSliceToSet(resp.ExcludedNetworkIDs))
d.Set("forward", resp.Forward)
d.Set("full_duplex", resp.FullDuplex)
d.Set("isolation", resp.Isolation)
d.Set("lldpmed_enabled", resp.LldpmedEnabled)
d.Set("lldpmed_notify_enabled", resp.LldpmedNotifyEnabled)
d.Set("native_networkconf_id", resp.NATiveNetworkID)
d.Set("name", resp.Name)
d.Set("op_mode", resp.OpMode)
d.Set("poe_mode", resp.PoeMode)
d.Set("port_security_enabled", resp.PortSecurityEnabled)
d.Set("port_security_mac_address", utils.StringSliceToSet(resp.PortSecurityMACAddress))
d.Set("priority_queue1_level", resp.PriorityQueue1Level)
d.Set("priority_queue2_level", resp.PriorityQueue2Level)
d.Set("priority_queue3_level", resp.PriorityQueue3Level)
d.Set("priority_queue4_level", resp.PriorityQueue4Level)
d.Set("speed", resp.Speed)
d.Set("stormctrl_bcast_enabled", resp.StormctrlBroadcastastEnabled)
d.Set("stormctrl_bcast_level", resp.StormctrlBroadcastastLevel)
d.Set("stormctrl_bcast_rate", resp.StormctrlBroadcastastRate)
d.Set("stormctrl_mcast_enabled", resp.StormctrlMcastEnabled)
d.Set("stormctrl_mcast_level", resp.StormctrlMcastLevel)
d.Set("stormctrl_mcast_rate", resp.StormctrlMcastRate)
d.Set("stormctrl_type", resp.StormctrlType)
d.Set("stormctrl_ucast_enabled", resp.StormctrlUcastEnabled)
d.Set("stormctrl_ucast_level", resp.StormctrlUcastLevel)
d.Set("stormctrl_ucast_rate", resp.StormctrlUcastRate)
d.Set("stp_port_mode", resp.StpPortMode)
d.Set("tagged_vlan_mgmt", resp.TaggedVLANMgmt)
d.Set("voice_networkconf_id", resp.VoiceNetworkID)
return nil
}
func resourcePortProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*provider.Client)
id := d.Id()
site := d.Get("site").(string)
if site == "" {
site = c.Site
}
resp, err := c.GetPortProfile(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) {
d.SetId("")
return nil
}
if err != nil {
return diag.FromErr(err)
}
return resourcePortProfileSetResourceData(resp, d, site)
}
func resourcePortProfileUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*provider.Client)
req, err := resourcePortProfileGetResourceData(d)
if err != nil {
return diag.FromErr(err)
}
req.ID = d.Id()
site := d.Get("site").(string)
if site == "" {
site = c.Site
}
req.SiteID = site
resp, err := c.UpdatePortProfile(ctx, site, req)
if err != nil {
return diag.FromErr(err)
}
return resourcePortProfileSetResourceData(resp, d, site)
}
func resourcePortProfileDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*provider.Client)
id := d.Id()
site := d.Get("site").(string)
if site == "" {
site = c.Site
}
err := c.DeletePortProfile(ctx, site, id)
return diag.FromErr(err)
}