Skip to content

Commit

Permalink
feat: add maas_vlan_dhcp resource
Browse files Browse the repository at this point in the history
  • Loading branch information
skatsaounis committed Dec 12, 2024
1 parent b76df54 commit 2534e62
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 18 deletions.
1 change: 0 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ The [VLAN](https://github.com/maas/terraform-provider-maas/blob/master/docs/data
A VLAN data source exports a few useful attributes:

- mtu - The MTU used on the VLAN.
- dhcp_on - Boolean value indicating if DHCP is enabled on the VLAN.
- name - The VLAN name.
- space - The VLAN space.

Expand Down
1 change: 0 additions & 1 deletion docs/resources/vlan.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ resource "maas_vlan" "tf_vlan" {

### Optional

- `dhcp_on` (Boolean) Boolean value. Whether or not DHCP should be managed on the new VLAN. This argument is computed if it's not set.
- `mtu` (Number) The MTU to use on the new VLAN. This argument is computed if it's not set.
- `name` (String) The name of the new VLAN. This argument is computed if it's not set.
- `space` (String) The space of the new VLAN. Passing in an empty string (or the string `undefined`) will cause the VLAN to be placed in the `undefined` space. This argument is computed if it's not set.
Expand Down
33 changes: 33 additions & 0 deletions docs/resources/vlan_dhcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "maas_vlan_dhcp Resource - terraform-provider-maas"
subcategory: ""
description: |-
Provides a resource to manage DHCP on MAAS network VLANs.
---

# maas_vlan_dhcp (Resource)

Provides a resource to manage DHCP on MAAS network VLANs.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `fabric` (Number) Database ID of the fabric of the VLAN whose DHCP is managed.
- `vlan` (Number) Database ID of the VLAN whose DHCP is managed.

### Optional

- `ip_ranges` (Set of Number) A set of IP range ids to server DHCP to. IP ranges must be of type dynamic.
- `primary_rack_controller` (String) The system_id of the Rack controller to to use as primary for DHCP.
- `relay_vlan` (Number) Database ID of the VLAN to to use as a relay for DHCP.
- `secondary_rack_controller` (String) The system_id of the Rack controller to to use as secondary for DHCP.
- `subnets` (Set of Number) A set of subnet ids to serve DHCP on their dynamic IP ranges.

### Read-Only

- `id` (String) The ID of this resource.
1 change: 1 addition & 0 deletions maas/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func Provider() *schema.Provider {
"maas_network_interface_link": resourceMaasNetworkInterfaceLink(),
"maas_fabric": resourceMaasFabric(),
"maas_vlan": resourceMaasVlan(),
"maas_vlan_dhcp": resourceMaasVlanDHCP(),
"maas_subnet": resourceMaasSubnet(),
"maas_subnet_ip_range": resourceMaasSubnetIPRange(),
"maas_dns_domain": resourceMaasDnsDomain(),
Expand Down
22 changes: 7 additions & 15 deletions maas/resource_maas_vlan.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@ func resourceMaasVlan() *schema.Resource {
},

Schema: map[string]*schema.Schema{
"dhcp_on": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
Description: "Boolean value. Whether or not DHCP should be managed on the new VLAN. This argument is computed if it's not set.",
},
"fabric": {
Type: schema.TypeString,
Required: true,
Expand Down Expand Up @@ -114,10 +108,9 @@ func resourceVlanRead(ctx context.Context, d *schema.ResourceData, meta interfac
return diag.FromErr(err)
}
tfState := map[string]interface{}{
"mtu": vlan.MTU,
"dhcp_on": vlan.DHCPOn,
"name": vlan.Name,
"space": vlan.Space,
"mtu": vlan.MTU,
"name": vlan.Name,
"space": vlan.Space,
}
if err := setTerraformState(d, tfState); err != nil {
return diag.FromErr(err)
Expand Down Expand Up @@ -164,11 +157,10 @@ func resourceVlanDelete(ctx context.Context, d *schema.ResourceData, meta interf

func getVlanParams(d *schema.ResourceData) *entity.VLANParams {
return &entity.VLANParams{
VID: d.Get("vid").(int),
MTU: d.Get("mtu").(int),
DHCPOn: d.Get("dhcp_on").(bool),
Name: d.Get("name").(string),
Space: d.Get("space").(string),
VID: d.Get("vid").(int),
MTU: d.Get("mtu").(int),
Name: d.Get("name").(string),
Space: d.Get("space").(string),
}
}

Expand Down
208 changes: 208 additions & 0 deletions maas/resource_maas_vlan_dhcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package maas

import (
"context"
"fmt"
"strconv"

"github.com/canonical/gomaasclient/client"
"github.com/canonical/gomaasclient/entity"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceMaasVlanDHCP() *schema.Resource {
return &schema.Resource{
Description: "Provides a resource to manage DHCP on MAAS network VLANs.",
CreateContext: resourceVlanDHCPCreate,
ReadContext: resourceVlanDHCPRead,
UpdateContext: resourceVlanDHCPUpdate,
DeleteContext: resourceVlanDHCPDelete,
Importer: &schema.ResourceImporter{
StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
return []*schema.ResourceData{d}, nil
},
},

Schema: map[string]*schema.Schema{
"fabric": {
Type: schema.TypeInt,
Required: true,
Description: "Database ID of the fabric of the VLAN whose DHCP is managed.",
},
"ip_ranges": {
Type: schema.TypeSet,
Optional: true,
Description: "A set of IP range ids to server DHCP to. IP ranges must be of type dynamic.",
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
"primary_rack_controller": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"relay_vlan"},
AtLeastOneOf: []string{"primary_rack_controller", "relay_vlan"},
Description: "The system_id of the Rack controller to to use as primary for DHCP.",
},
"relay_vlan": {
Type: schema.TypeInt,
Optional: true,
ConflictsWith: []string{"primary_rack_controller", "secondary_rack_controller"},
AtLeastOneOf: []string{"primary_rack_controller", "relay_vlan"},
Description: "Database ID of the VLAN to to use as a relay for DHCP.",
},
"secondary_rack_controller": {
Type: schema.TypeString,
Optional: true,
RequiredWith: []string{"primary_rack_controller"},
ConflictsWith: []string{"relay_vlan"},
Description: "The system_id of the Rack controller to to use as secondary for DHCP.",
},
"subnets": {
Type: schema.TypeSet,
Optional: true,
Description: "A set of subnet ids to serve DHCP on their dynamic IP ranges.",
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
"vlan": {
Type: schema.TypeInt,
Required: true,
Description: "Database ID of the VLAN whose DHCP is managed.",
},
},
}
}

func resourceVlanDHCPCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*client.Client)

err := confirmAllIPRangesDynamic(client, d)
if err != nil {
return diag.FromErr(err)
}

err = confirmAllSubnetsWithADynamicIPRange(client, d)
if err != nil {
return diag.FromErr(err)
}

fabricID := d.Get("fabric").(int)
vlanID := d.Get("vlan").(int)
_, err = client.VLAN.Update(fabricID, vlanID, getVlanDHCPParams(d))
if err != nil {
return diag.FromErr(err)
}
d.SetId(strconv.Itoa(vlanID))

return nil
}

func resourceVlanDHCPRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*client.Client)

id, err := strconv.Atoi(d.Id())
if err != nil {
return diag.FromErr(err)
}
ipRange, err := client.IPRange.Get(id)
if err != nil {
return diag.FromErr(err)
}
tfState := map[string]interface{}{
"comment": ipRange.Comment,
}
if err := setTerraformState(d, tfState); err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceVlanDHCPUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*client.Client)

vlanID, err := strconv.Atoi(d.Id())
if err != nil {
return diag.FromErr(err)
}
fabricID := d.Get("fabric").(int)
if _, err := client.VLAN.Update(fabricID, vlanID, getVlanDHCPParams(d)); err != nil {
return diag.FromErr(err)
}

return resourceVlanDHCPRead(ctx, d, meta)
}

func resourceVlanDHCPDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*client.Client)

fabricID := d.Get("fabric").(int)
vlanID := d.Get("vlan").(int)
_, err := client.VLAN.Update(fabricID, vlanID, &entity.VLANParams{
PrimaryRack: "", SecondaryRack: "", RelayVLAN: 0,
})
if err != nil {
return diag.FromErr(err)
}

return nil
}

func getVlanDHCPParams(d *schema.ResourceData) *entity.VLANParams {
vlanParams := entity.VLANParams{
DHCPOn: true,
}
if v, ok := d.GetOk("primary_rack_controller"); ok {
vlanParams.PrimaryRack = v.(string)
}
if v, ok := d.GetOk("secondary_rack_controller"); ok {
vlanParams.SecondaryRack = v.(string)
}
if v, ok := d.GetOk("relay_vlan"); ok {
vlanParams.RelayVLAN = v.(int)
}
return &vlanParams
}

func confirmAllSubnetsWithADynamicIPRange(client *client.Client, d *schema.ResourceData) error {
for _, subnetID := range d.Get("subnets").(*schema.Set).List() {
subnetIPRanges, err := client.Subnet.GetReservedIPRanges(subnetID.(int))
if err != nil {
return err
}
foundDynamic := false
for _, ipRange := range subnetIPRanges {
for _, purpose := range ipRange.Purpose {
if purpose == "dynamic" {
foundDynamic = true
break
}
}
if foundDynamic {
break
}
}
if !foundDynamic {
return fmt.Errorf("subnet %s does not have any dynamic IP range", subnetID)
}
}

return nil
}

func confirmAllIPRangesDynamic(client *client.Client, d *schema.ResourceData) error {
for _, ipRangeID := range d.Get("ip_ranges").(*schema.Set).List() {
ipRange, err := client.IPRange.Get(ipRangeID.(int))
if err != nil {
return err
}
if ipRange.Type != "dynamic" {
return fmt.Errorf("IP range %s is not dynamic", ipRangeID)
}
}

return nil
}
1 change: 0 additions & 1 deletion templates/index.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ The [VLAN](https://github.com/maas/terraform-provider-maas/blob/master/docs/data
A VLAN data source exports a few useful attributes:

- mtu - The MTU used on the VLAN.
- dhcp_on - Boolean value indicating if DHCP is enabled on the VLAN.
- name - The VLAN name.
- space - The VLAN space.

Expand Down

0 comments on commit 2534e62

Please sign in to comment.