Skip to content

Commit

Permalink
feat: Allow prefix and ip to be dynamically assigned
Browse files Browse the repository at this point in the history
  • Loading branch information
amhn committed Sep 23, 2022
1 parent 8daaae0 commit 88cf8e9
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 27 deletions.
18 changes: 9 additions & 9 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ import (
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs

func main() {
var debug bool
var debug bool

flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve")
flag.Parse()
flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve")
flag.Parse()

opts := &plugin.ServeOpts{
Debug: debug,
ProviderAddr: "registry.terraform.io/smutel/netbox",
ProviderFunc: netbox.Provider,
}
opts := &plugin.ServeOpts{
Debug: debug,
ProviderAddr: "registry.terraform.io/smutel/netbox",
ProviderFunc: netbox.Provider,
}

plugin.Serve(opts)
plugin.Serve(opts)
}
67 changes: 57 additions & 10 deletions netbox/resource_netbox_ipam_ip_addresses.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,23 @@ func resourceNetboxIpamIPAddresses() *schema.Resource {
Schema: map[string]*schema.Schema{
"address": {
Type: schema.TypeString,
Required: true,
Computed: true,
Optional: true,
ExactlyOneOf: []string{"address", "prefix", "ip_range"},
ValidateFunc: validation.IsCIDR,
Description: "The IP address (with mask) used for this IP address (ipam module).",
Description: "The IP address (with mask) used for this IP address (ipam module). Required if both prefix and ip_range are not set.",
},
"ip_range": {
Type: schema.TypeInt,
ForceNew: true,
Optional: true,
Description: "The ip-range id for automatic IP assignment. Required if both prefix and address are not set.",
},
"prefix": {
Type: schema.TypeInt,
ForceNew: true,
Optional: true,
Description: "The prefix id for automatic IP assignment. Required if both address and ip_range are not set.",
},
"content_type": {
Type: schema.TypeString,
Expand Down Expand Up @@ -159,7 +173,30 @@ func resourceNetboxIpamIPAddressesCreate(ctx context.Context, d *schema.Resource
m interface{}) diag.Diagnostics {
client := m.(*netboxclient.NetBoxAPI)

address := d.Get("address").(string)
var address string
var addressid *int64
if stateaddress, ok := d.GetOk("address"); ok {
address = stateaddress.(string)
} else if prefixid, ok := d.GetOk("prefix"); ok {
ip, err := getNewAvailableIPForPrefix(client, int64(prefixid.(int)))
if err != nil {
return diag.FromErr(err)
}
address = *ip.Address
addressid = &ip.ID
d.Set("address", address)
} else if rangeid, ok := d.GetOk("ip_range"); ok {
ip, err := getNewAvailableIPForIPRange(client, int64(rangeid.(int)))
if err != nil {
return diag.FromErr(err)
}
address = *ip.Address
addressid = &ip.ID
d.Set("address", address)
} else {
return diag.Errorf("exactly one of (address, ip_range, prefix) must be specified")
}

resourceCustomFields := d.Get("custom_field").(*schema.Set).List()
customFields := convertCustomFieldsFromTerraformToAPI(nil, resourceCustomFields)
description := d.Get("description").(string)
Expand Down Expand Up @@ -212,16 +249,26 @@ func resourceNetboxIpamIPAddressesCreate(ctx context.Context, d *schema.Resource
newResource.Vrf = &vrfID
}

resource := ipam.NewIpamIPAddressesCreateParams().WithData(newResource)
if addressid == nil {
resource := ipam.NewIpamIPAddressesCreateParams().WithData(newResource)

resourceCreated, err := client.Ipam.IpamIPAddressesCreate(resource, nil)
if err != nil {
return diag.FromErr(err)
}
resourceCreated, err := client.Ipam.IpamIPAddressesCreate(resource, nil)
if err != nil {
return diag.FromErr(err)
}

addressid = &resourceCreated.Payload.ID
} else {
resource := ipam.NewIpamIPAddressesUpdateParams().WithID(*addressid).WithData(newResource)

d.SetId(strconv.FormatInt(resourceCreated.Payload.ID, 10))
_, err := client.Ipam.IpamIPAddressesUpdate(resource, nil)
if err != nil {
return diag.FromErr(err)
}
}

err = updatePrimaryStatus(client, info, resourceCreated.Payload.ID)
d.SetId(strconv.FormatInt(*addressid, 10))
err := updatePrimaryStatus(client, info, *addressid)
if err != nil {
return diag.FromErr(err)
}
Expand Down
70 changes: 62 additions & 8 deletions netbox/resource_netbox_ipam_prefix.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,32 @@ func resourceNetboxIpamPrefix() *schema.Resource {
},
"prefix": {
Type: schema.TypeString,
Required: true,
Optional: true,
Computed: true,
ValidateFunc: validation.IsCIDRNetwork(0, 256),
Description: "The prefix (IP address/mask) used for this prefix (ipam module).",
ExactlyOneOf: []string{"prefix", "parent_prefix"},
Description: "The prefix (IP address/mask) used for this prefix (ipam module). Required if parent_prefix is not set.",
},
"parent_prefix": {
Type: schema.TypeSet,
Optional: true,
MaxItems: 1,
Description: "Parent prefix and length used for new prefix. Required if prefix is not set",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"prefix": {
Type: schema.TypeInt,
Required: true,
Description: "Id of parent prefix",
},
"prefix_length": {
Type: schema.TypeInt,
Required: true,
Description: "Length of new prefix",
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(0, 128)),
},
},
},
},
"role_id": {
Type: schema.TypeInt,
Expand Down Expand Up @@ -135,11 +158,29 @@ func resourceNetboxIpamPrefixCreate(ctx context.Context, d *schema.ResourceData,
m interface{}) diag.Diagnostics {
client := m.(*netboxclient.NetBoxAPI)

var prefix string
var prefixid *int64
if stateprefix, ok := d.GetOk("prefix"); ok {
prefix = stateprefix.(string)
} else if pprefix, ok := d.GetOk("parent_prefix"); ok {
set := pprefix.(*schema.Set)
mappreffix := set.List()[0].(map[string]interface{})
parentPrefix := int64(mappreffix["prefix"].(int))
prefixlength := int64(mappreffix["prefix_length"].(int))
p, err := getNewAvailablePrefix(client, parentPrefix, prefixlength)
if err != nil {
return diag.FromErr(err)
}
prefix = *p.Prefix
prefixid = &p.ID
} else {
return diag.Errorf("exactly one of (prefix, parent_prefix) must be specified")
}

resourceCustomFields := d.Get("custom_field").(*schema.Set).List()
customFields := convertCustomFieldsFromTerraformToAPI(nil, resourceCustomFields)
description := d.Get("description").(string)
isPool := d.Get("is_pool").(bool)
prefix := d.Get("prefix").(string)
roleID := int64(d.Get("role_id").(int))
siteID := int64(d.Get("site_id").(int))
status := d.Get("status").(string)
Expand Down Expand Up @@ -177,14 +218,27 @@ func resourceNetboxIpamPrefixCreate(ctx context.Context, d *schema.ResourceData,
newResource.Vrf = &vrfID
}

resource := ipam.NewIpamPrefixesCreateParams().WithData(newResource)
if prefixid == nil {
resource := ipam.NewIpamPrefixesCreateParams().WithData(newResource)

resourceCreated, err := client.Ipam.IpamPrefixesCreate(resource, nil)
if err != nil {
return diag.FromErr(err)
resourceCreated, err := client.Ipam.IpamPrefixesCreate(resource, nil)
if err != nil {
return diag.FromErr(err)
}

prefixid = &resourceCreated.Payload.ID
} else {
resource := ipam.NewIpamPrefixesUpdateParams().WithID(*prefixid).WithData(newResource)

resourceCreated, err := client.Ipam.IpamPrefixesUpdate(resource, nil)
if err != nil {
return diag.FromErr(err)
}

prefixid = &resourceCreated.Payload.ID
}

d.SetId(strconv.FormatInt(resourceCreated.Payload.ID, 10))
d.SetId(strconv.FormatInt(*prefixid, 10))

return resourceNetboxIpamPrefixRead(ctx, d, m)
}
Expand Down
37 changes: 37 additions & 0 deletions netbox/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/go-openapi/strfmt"
netboxclient "github.com/smutel/go-netbox/v3/netbox/client"
"github.com/smutel/go-netbox/v3/netbox/client/ipam"
"github.com/smutel/go-netbox/v3/netbox/client/virtualization"
"github.com/smutel/go-netbox/v3/netbox/models"
)
Expand Down Expand Up @@ -272,3 +273,39 @@ func convertURIContentType(uri strfmt.URI) string {
contentType := firstLevel + "." + secondLevel
return contentType
}

func getNewAvailableIPForIPRange(client *netboxclient.NetBoxAPI, id int64) (*models.IPAddress, error) {
params := ipam.NewIpamIPRangesAvailableIpsCreateParams().WithID(id)
params.Data = []*models.WritableAvailableIP{
{},
}
list, err := client.Ipam.IpamIPRangesAvailableIpsCreate(params, nil)
if err != nil {
return nil, err
}
return list.Payload[0], nil
}

func getNewAvailableIPForPrefix(client *netboxclient.NetBoxAPI, id int64) (*models.IPAddress, error) {
params := ipam.NewIpamPrefixesAvailableIpsCreateParams().WithID(id)
params.Data = []*models.WritableAvailableIP{
{},
}
list, err := client.Ipam.IpamPrefixesAvailableIpsCreate(params, nil)
if err != nil {
return nil, err
}
return list.Payload[0], nil
}

func getNewAvailablePrefix(client *netboxclient.NetBoxAPI, id int64, length int64) (*models.Prefix, error) {
params := ipam.NewIpamPrefixesAvailablePrefixesCreateParams().WithID(id)
params.Data = []*models.PrefixLength{
{PrefixLength: &length},
}
list, err := client.Ipam.IpamPrefixesAvailablePrefixesCreate(params, nil)
if err != nil {
return nil, err
}
return list.Payload[0], nil
}

0 comments on commit 88cf8e9

Please sign in to comment.