diff --git a/docs/resources/network.md b/docs/resources/network.md index 7c3a76ae6..d3301c943 100644 --- a/docs/resources/network.md +++ b/docs/resources/network.md @@ -52,6 +52,7 @@ resource "unifi_network" "wan" { ### Optional - `dhcp_dns` (List of String) Specifies the IPv4 addresses for the DNS server to be returned from the DHCP server. Leave blank to disable this feature. +- `dhcp_ntp` (List of String) Specified the IPv4 addresses for the NTP servers to be returned from the DHCP server in response to an Option 42 (NTP Servers) request. Leave blank to disable this feature. - `dhcp_enabled` (Boolean) Specifies whether DHCP is enabled or not on this network. - `dhcp_lease` (Number) Specifies the lease time for DHCP addresses in seconds. Defaults to `86400`. - `dhcp_relay_enabled` (Boolean) Specifies whether DHCP relay is enabled or not on this network. diff --git a/internal/provider/resource_network.go b/internal/provider/resource_network.go index 3faf0b8c7..5548383b7 100644 --- a/internal/provider/resource_network.go +++ b/internal/provider/resource_network.go @@ -120,6 +120,19 @@ func resourceNetwork() *schema.Resource { Optional: true, Default: 86400, }, + "dhcp_ntp": { + Description: "Specified the address for the NTP server to be returned from the DHCP server.", + Type: schema.TypeList, + Optional: true, + MaxItems: 2, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.All( + validation.IsIPv4Address, + validation.StringLenBetween(1, 50), + ), + }, + }, "dhcp_dns": { Description: "Specifies the IPv4 addresses for the DNS server to be returned from the DHCP " + "server. Leave blank to disable this feature.", @@ -405,6 +418,10 @@ func resourceNetworkGetResourceData(d *schema.ResourceData, meta interface{}) (* if err != nil { return nil, fmt.Errorf("unable to convert dhcp_dns to string slice: %w", err) } + dhcpNtp, err := listToStringSlice(d.Get("dhcp_ntp").([]interface{})) + if err != nil { + return nil, fmt.Errorf("unable to convert dhcp_ntp to string slice: %w", err) + } dhcpV6DNS, err := listToStringSlice(d.Get("dhcp_v6_dns").([]interface{})) if err != nil { return nil, fmt.Errorf("unable to convert dhcp_v6_dns to string slice: %w", err) @@ -439,6 +456,10 @@ func resourceNetworkGetResourceData(d *schema.ResourceData, meta interface{}) (* DHCPDDNS3: append(dhcpDNS, "", "", "")[2], DHCPDDNS4: append(dhcpDNS, "", "", "", "")[3], + DHCPDNtpEnabled: len(dhcpNtp) > 0, + DHCPDNtp1: append(dhcpNtp, "")[0], + DHCPDNtp2: append(dhcpNtp, "", "")[1], + VLANEnabled: vlan != 0 && vlan != 1, Enabled: true, @@ -533,6 +554,19 @@ func resourceNetworkSetResourceData(resp *unifi.Network, d *schema.ResourceData, dhcpLease = 86400 } + dhcpNTP := []string{} + if resp.DHCPDNtpEnabled { + for _, ntp := range []string{ + resp.DHCPDNtp1, + resp.DHCPDNtp2, + } { + if ntp == "" { + continue + } + dhcpNTP = append(dhcpNTP, ntp) + } + } + dhcpDNS := []string{} if resp.DHCPDDNSEnabled { for _, dns := range []string{ @@ -568,6 +602,7 @@ func resourceNetworkSetResourceData(resp *unifi.Network, d *schema.ResourceData, d.Set("subnet", cidrZeroBased(resp.IPSubnet)) d.Set("network_group", resp.NetworkGroup) + d.Set("dhcp_ntp", dhcpNTP) d.Set("dhcp_dns", dhcpDNS) d.Set("dhcp_enabled", resp.DHCPDEnabled) d.Set("dhcp_lease", dhcpLease) diff --git a/internal/provider/resource_network_test.go b/internal/provider/resource_network_test.go index 7dfd97d75..460da7c4e 100644 --- a/internal/provider/resource_network_test.go +++ b/internal/provider/resource_network_test.go @@ -23,7 +23,7 @@ func TestAccNetwork_basic(t *testing.T) { // TODO: CheckDestroy: , Steps: []resource.TestStep{ { - Config: testAccNetworkConfig(name, subnet1, vlan1, true, nil), + Config: testAccNetworkConfig(name, subnet1, vlan1, true, nil, nil), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("unifi_network.test", "domain_name", "foo.local"), resource.TestCheckResourceAttr("unifi_network.test", "vlan_id", strconv.Itoa(vlan1)), @@ -32,7 +32,7 @@ func TestAccNetwork_basic(t *testing.T) { }, importStep("unifi_network.test"), { - Config: testAccNetworkConfig(name, subnet2, vlan2, false, nil), + Config: testAccNetworkConfig(name, subnet2, vlan2, false, nil, nil), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("unifi_network.test", "vlan_id", strconv.Itoa(vlan2)), resource.TestCheckResourceAttr("unifi_network.test", "igmp_snooping", "false"), @@ -60,7 +60,7 @@ func TestAccNetwork_weird_cidr(t *testing.T) { // TODO: CheckDestroy: , Steps: []resource.TestStep{ { - Config: testAccNetworkConfig(name, subnet, vlan, true, nil), + Config: testAccNetworkConfig(name, subnet, vlan, true, nil, nil), Check: resource.ComposeTestCheckFunc( // TODO: ... ), @@ -80,14 +80,14 @@ func TestAccNetwork_dhcp_dns(t *testing.T) { // TODO: CheckDestroy: , Steps: []resource.TestStep{ { - Config: testAccNetworkConfig(name, subnet, vlan, true, []string{"192.168.1.101"}), + Config: testAccNetworkConfig(name, subnet, vlan, true, []string{"192.168.1.101"}, nil), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("unifi_network.test", "dhcp_dns.0", "192.168.1.101"), ), }, importStep("unifi_network.test"), { - Config: testAccNetworkConfig(name, subnet, vlan, true, []string{"192.168.1.101", "192.168.1.102"}), + Config: testAccNetworkConfig(name, subnet, vlan, true, []string{"192.168.1.101", "192.168.1.102"}, nil), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("unifi_network.test", "dhcp_dns.0", "192.168.1.101"), resource.TestCheckResourceAttr("unifi_network.test", "dhcp_dns.1", "192.168.1.102"), @@ -95,13 +95,13 @@ func TestAccNetwork_dhcp_dns(t *testing.T) { }, importStep("unifi_network.test"), { - Config: testAccNetworkConfig(name, subnet, vlan, true, nil), + Config: testAccNetworkConfig(name, subnet, vlan, true, nil, nil), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("unifi_network.test", "dhcp_dns.#", "0"), ), }, { - Config: testAccNetworkConfig(name, subnet, vlan, true, []string{"192.168.1.101"}), + Config: testAccNetworkConfig(name, subnet, vlan, true, []string{"192.168.1.101"}, nil), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("unifi_network.test", "dhcp_dns.0", "192.168.1.101"), ), @@ -110,6 +110,25 @@ func TestAccNetwork_dhcp_dns(t *testing.T) { }) } +func TestAccNetwork_dhcp_ntp(t *testing.T) { + name := acctest.RandomWithPrefix("tfacc") + subnet, vlan := getTestVLAN(t) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { preCheck(t) }, + ProviderFactories: providerFactories, + // TODO: CheckDestroy: , + Steps: []resource.TestStep{ + { + Config: testAccNetworkConfig(name, subnet, vlan, true, nil, []string{"192.168.1.123"}), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("unifi_network.test", "dhcp_ntp.0", "192.168.1.123"), + ), + }, + }, + }) +} + func TestAccNetwork_dhcp_boot(t *testing.T) { name := acctest.RandomWithPrefix("tfacc") subnet, vlan := getTestVLAN(t) @@ -312,10 +331,10 @@ func TestAccNetwork_importByName(t *testing.T) { Steps: []resource.TestStep{ // Apply and import network by name. { - Config: testAccNetworkConfig(name, subnet1, vlan1, true, nil), + Config: testAccNetworkConfig(name, subnet1, vlan1, true, nil, nil), }, { - Config: testAccNetworkConfig(name, subnet1, vlan1, true, nil), + Config: testAccNetworkConfig(name, subnet1, vlan1, true, nil, nil), ResourceName: "unifi_network.test", ImportState: true, ImportStateVerify: true, @@ -470,7 +489,7 @@ resource "unifi_network" "test" { `, name, subnet, vlan) } -func testAccNetworkConfig(name string, subnet *net.IPNet, vlan int, igmpSnoop bool, dhcpDNS []string) string { +func testAccNetworkConfig(name string, subnet *net.IPNet, vlan int, igmpSnoop bool, dhcpDNS []string, dhcpNTP []string) string { return fmt.Sprintf(` locals { subnet = "%[2]s" @@ -490,8 +509,9 @@ resource "unifi_network" "test" { igmp_snooping = %[4]t dhcp_dns = [%[5]s] + dhcp_ntp = [%[6]s] } -`, name, subnet, vlan, igmpSnoop, strings.Join(quoteStrings(dhcpDNS), ",")) +`, name, subnet, vlan, igmpSnoop, strings.Join(quoteStrings(dhcpDNS), ","), strings.Join(quoteStrings(dhcpNTP), ",")) } func testAccNetworkConfigV6(name string, subnet *net.IPNet, vlan int, ipv6Type string, ipv6Subnet string) string {