diff --git a/docs/resources/wlan.md b/docs/resources/wlan.md index 73c9051..5e3039e 100644 --- a/docs/resources/wlan.md +++ b/docs/resources/wlan.md @@ -67,6 +67,8 @@ resource "unifi_wlan" "wifi" { - **mac_filter_enabled** (Boolean) Indicates whether or not the MAC filter is turned of for the network. - **mac_filter_list** (Set of String) List of MAC addresses to filter (only valid if `mac_filter_enabled` is `true`). - **mac_filter_policy** (String) MAC address filter policy (only valid if `mac_filter_enabled` is `true`). Defaults to `deny`. +- **minimum_data_rate_2g_kbps** (Number) Set minimum data rate control for 2G devices, in Kbps. Use `0` to disable minimum data rates. Valid values are: `1000`, `2000`, `5500`, `6000`, `9000`, `11000`, `12000`, `18000`, `24000`, `36000`, `48000`, and `54000`. +- **minimum_data_rate_5g_kbps** (Number) Set minimum data rate control for 5G devices, in Kbps. Use `0` to disable minimum data rates. Valid values are: `6000`, `9000`, `12000`, `18000`, `24000`, `36000`, `48000`, and `54000`. - **multicast_enhance** (Boolean) Indicates whether or not Multicast Enhance is turned of for the network. - **network_id** (String) ID of the network for this SSID - **no2ghz_oui** (Boolean) Connect high performance clients to 5 GHz only Defaults to `true`. diff --git a/internal/provider/markdown.go b/internal/provider/markdown.go new file mode 100644 index 0000000..2407bb5 --- /dev/null +++ b/internal/provider/markdown.go @@ -0,0 +1,19 @@ +package provider + +import "strconv" + +func markdownValueListInt(values []int) string { + switch { + case len(values) == 0: + return "" + case len(values) == 1: + return "`" + strconv.Itoa(values[0]) + "`" + default: + s := "" + for i := 0; i < len(values)-1; i++ { + s += "`" + strconv.Itoa(values[i]) + "`, " + } + s += " and `" + strconv.Itoa(values[len(values)-1]) + "`" + return s + } +} diff --git a/internal/provider/resource_wlan.go b/internal/provider/resource_wlan.go index 4f05a58..a7ed049 100644 --- a/internal/provider/resource_wlan.go +++ b/internal/provider/resource_wlan.go @@ -12,6 +12,11 @@ import ( "github.com/paultyng/go-unifi/unifi" ) +var ( + wlanValidMinimumDataRate2g = []int{1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000, 54000} + wlanValidMinimumDataRate5g = []int{6000, 9000, 12000, 18000, 24000, 36000, 48000, 54000} +) + func resourceWLAN() *schema.Resource { return &schema.Resource{ Description: "`unifi_wlan` manages a WiFi network / SSID.", @@ -160,6 +165,24 @@ func resourceWLAN() *schema.Resource { Optional: true, Default: false, }, + "minimum_data_rate_2g_kbps": { + Description: "Set minimum data rate control for 2G devices, in Kbps. " + + "Use `0` to disable minimum data rates. " + + "Valid values are: " + markdownValueListInt(wlanValidMinimumDataRate2g) + ".", + Type: schema.TypeInt, + Optional: true, + // TODO: this validation is from the UI, if other values work, perhaps remove this is set it to a range instead? + ValidateFunc: validation.IntInSlice(append([]int{0}, wlanValidMinimumDataRate2g...)), + }, + "minimum_data_rate_5g_kbps": { + Description: "Set minimum data rate control for 5G devices, in Kbps. " + + "Use `0` to disable minimum data rates. " + + "Valid values are: " + markdownValueListInt(wlanValidMinimumDataRate5g) + ".", + Type: schema.TypeInt, + Optional: true, + // TODO: this validation is from the UI, if other values work, perhaps remove this is set it to a range instead? + ValidateFunc: validation.IntInSlice(append([]int{0}, wlanValidMinimumDataRate5g...)), + }, // controller v6 fields // TODO: this could be defaulted to "both" once v5 controller support is dropped @@ -307,11 +330,18 @@ func resourceWLANGetResourceData(d *schema.ResourceData, meta interface{}) (*uni Enabled: true, NameCombineEnabled: true, - GroupRekey: 3600, - DTIMMode: "default", - No2GhzOui: d.Get("no2ghz_oui").(bool), - L2Isolation: d.Get("l2_isolation").(bool), - UapsdEnabled: d.Get("uapsd").(bool), + GroupRekey: 3600, + DTIMMode: "default", + No2GhzOui: d.Get("no2ghz_oui").(bool), + L2Isolation: d.Get("l2_isolation").(bool), + UapsdEnabled: d.Get("uapsd").(bool), + + MinrateNgEnabled: d.Get("minimum_data_rate_2g_kbps").(int) != 0, + MinrateNgDataRateKbps: d.Get("minimum_data_rate_2g_kbps").(int), + + MinrateNaEnabled: d.Get("minimum_data_rate_5g_kbps").(int) != 0, + MinrateNaDataRateKbps: d.Get("minimum_data_rate_5g_kbps").(int), + MinrateNgCckRatesEnabled: true, }, nil } @@ -394,6 +424,16 @@ func resourceWLANSetResourceData(resp *unifi.WLAN, d *schema.ResourceData, meta d.Set("no2ghz_oui", resp.No2GhzOui) d.Set("l2_isolation", resp.L2Isolation) d.Set("uapsd", resp.UapsdEnabled) + if resp.MinrateNgEnabled { + d.Set("minimum_data_rate_2g_kbps", resp.MinrateNgDataRateKbps) + } else { + d.Set("minimum_data_rate_2g_kbps", 0) + } + if resp.MinrateNaEnabled { + d.Set("minimum_data_rate_5g_kbps", resp.MinrateNaDataRateKbps) + } else { + d.Set("minimum_data_rate_5g_kbps", 0) + } // switch v := c.ControllerVersion(); { // case v.GreaterThanOrEqual(controllerV6): diff --git a/internal/provider/resource_wlan_test.go b/internal/provider/resource_wlan_test.go index 47a3fe2..0a010bd 100644 --- a/internal/provider/resource_wlan_test.go +++ b/internal/provider/resource_wlan_test.go @@ -327,6 +327,54 @@ func TestAccWLAN_wpa3(t *testing.T) { }) } +func TestAccWLAN_minimum_data_rate(t *testing.T) { + vlanID := getTestVLAN(t) + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + preCheck(t) + preCheckV6Only(t) + wlanPreCheck(t) + }, + ProviderFactories: providerFactories, + CheckDestroy: func(*terraform.State) error { + // TODO: actual CheckDestroy + + <-wlanConcurrency + return nil + }, + Steps: []resource.TestStep{ + { + Config: testAccWLANConfig_minimum_data_rate(vlanID, 5500, 18000), + Check: resource.ComposeTestCheckFunc( + // testCheckNetworkExists(t, "name"), + ), + }, + importStep("unifi_wlan.test"), + { + Config: testAccWLANConfig_minimum_data_rate(vlanID, 0, 18000), + Check: resource.ComposeTestCheckFunc( + // testCheckNetworkExists(t, "name"), + ), + }, + importStep("unifi_wlan.test"), + { + Config: testAccWLANConfig_minimum_data_rate(vlanID, 6000, 0), + Check: resource.ComposeTestCheckFunc( + // testCheckNetworkExists(t, "name"), + ), + }, + importStep("unifi_wlan.test"), + { + Config: testAccWLANConfig_minimum_data_rate(vlanID, 18000, 6000), + Check: resource.ComposeTestCheckFunc( + // testCheckNetworkExists(t, "name"), + ), + }, + importStep("unifi_wlan.test"), + }, + }) +} + func testAccWLANConfig_wpapsk(vlanID int) string { return fmt.Sprintf(` data "unifi_ap_group" "default" { @@ -599,3 +647,35 @@ resource "unifi_wlan" "test" { } `, vlanID, wpa3Transition) } + +func testAccWLANConfig_minimum_data_rate(vlanID int, min2g int, min5g int) string { + return fmt.Sprintf(` +data "unifi_ap_group" "default" { +} + +data "unifi_user_group" "default" { +} + +resource "unifi_network" "test" { + name = "tfacc" + purpose = "corporate" + + subnet = cidrsubnet("10.0.0.0/8", 6, %[1]d) + vlan_id = %[1]d +} + +resource "unifi_wlan" "test" { + name = "tfacc-wpapsk" + network_id = unifi_network.test.id + passphrase = "12345678" + ap_group_ids = [data.unifi_ap_group.default.id] + user_group_id = data.unifi_user_group.default.id + security = "wpapsk" + + multicast_enhance = true + + minimum_data_rate_2g_kbps = %[2]d + minimum_data_rate_5g_kbps = %[3]d +} +`, vlanID, min2g, min5g) +}