Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adding support for fetching avalible ip from multiple subnets. #580

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7a981b0
Init commit on fork.
sanderskjulsvik May 2, 2024
9a088de
moved new resource into netbox
sanderskjulsvik May 2, 2024
00d959c
started adding tests from none multiple cidrs address
sanderskjulsvik May 2, 2024
be2a667
Tests are now working but failing
sanderskjulsvik May 3, 2024
4a0efd7
Basic test now runs
sanderskjulsvik May 3, 2024
d821313
Move-multiple-cidrs-into-avalible-ip
sanderskjulsvik May 3, 2024
ec533c7
Merge pull request #1 from pexip/Move-multiple-cidrs-into-avalible-ip
sanderskjulsvik May 3, 2024
63e4c45
removed multiple cidr from provider
sanderskjulsvik May 3, 2024
d040b92
Fixed name of new test to not have the multiple cidrs names
sanderskjulsvik May 3, 2024
b923182
added function assertInterfaceToInt64Slice, or converting interface{}…
sanderskjulsvik May 3, 2024
ddfb7cd
added selected_id to the schema.
sanderskjulsvik May 3, 2024
5091a2d
Added TestAccNetboxAvailableIPAddress_multiple_cidrs_overflow
sanderskjulsvik May 3, 2024
9bdc6f6
Added more tests including for ranges. But getting segfault
sanderskjulsvik May 3, 2024
3ba4063
Collected all payloads in create to an interfcase and now handeling t…
sanderskjulsvik May 6, 2024
fd63ac9
renamed payloadHandler to createPayloadHandler
sanderskjulsvik May 6, 2024
4065bac
Payload is nill for TestAccNetboxAvailableIPAddress_multiple_cidrs_ra…
sanderskjulsvik May 6, 2024
284edf8
Fixed typo in create for ranges. And scoped the vars so it is harder …
sanderskjulsvik May 6, 2024
c017da1
Cleaned expected ips in tests
sanderskjulsvik May 6, 2024
077277b
Added Validation to ip range start and end. If ip does not have prefi…
sanderskjulsvik May 6, 2024
13ca4c2
Updated validatation of resourceNetboxIPRange from to address to use …
sanderskjulsvik May 7, 2024
78d12b7
moved ip addresses around not to cause conflicts. aso renamed functio…
sanderskjulsvik May 7, 2024
dd42538
Merge branch 'e-breuninger:master' into master
sanderskjulsvik May 7, 2024
e5bb954
format: error strings should not end with punctuation or newlines
sanderskjulsvik May 7, 2024
f22e17a
fmt: rm 1 white space
sanderskjulsvik May 8, 2024
b095794
Merge branch 'e-breuninger:master' into master
sanderskjulsvik May 13, 2024
3d2dda0
added some readme to explaining multiple subnets and prefixes
sanderskjulsvik May 13, 2024
98824a1
Fixed linting error and ran CI lint locally and it seems happy now
sanderskjulsvik May 14, 2024
e2b4339
Merge branch 'e-breuninger:master' into master
sanderskjulsvik Jun 10, 2024
911a2dd
Merge branch 'e-breuninger:master' into master
sanderskjulsvik Jul 15, 2024
71004b1
Merge branch 'e-breuninger:master' into master
sanderskjulsvik Aug 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 34 additions & 5 deletions docs/resources/available_ip_address.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Per [the docs](https://netbox.readthedocs.io/en/stable/models/ipam/ipaddress/):
> * DHCP
> * SLAAC (IPv6 Stateless Address Autoconfiguration)

This resource will retrieve the next available IP address from a given prefix or IP range (specified by ID)
This resource will retrieve the next available IP address from a given prefix, IP range, prefixes or IP ranges (specified by ID)

## Example Usage
### Creating an IP in a prefix
Expand Down Expand Up @@ -77,6 +77,34 @@ resource "netbox_available_ip_address" "myvm-ip" {
}
```

### Creating Ip addreses from multiple subnets and ranges

```terraform
data "netbox_prefix" "test" {
count = 3
cidr = "10.&{count.index}.0.0/30"
is_pool = true
}

data "netbox_ip_range" "test" {
count = 3
start_address = "2.0.${count.index}.0"
end_address = "2.0.${count.index}.3"
}

resource "netbox_available_ip_address" "from_prefix" {
count = 3 * 4
prefix_id = data.netbox_prefix.test[count.index].id
status = "active"
}

resource "netbox_available_ip_address" "from_range" {
count = 3 * 4
ip_range_id = data.netbox_ip_range.test[count.index].id
status = "active"
}
```

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

Expand All @@ -86,9 +114,11 @@ resource "netbox_available_ip_address" "myvm-ip" {
- `device_interface_id` (Number) Conflicts with `interface_id` and `virtual_machine_interface_id`.
- `dns_name` (String)
- `interface_id` (Number) Required when `object_type` is set.
- `ip_range_id` (Number) Exactly one of `prefix_id` or `ip_range_id` must be given.
- `ip_range_id` (Number) Exactly one of `prefix_id`, `ip_range_id`, `prefix_ids` or `ip_range_ids` must be given.
- `ip_range_ids` (List(number)) Exactly one of `prefix_id`, `ip_range_id`, `prefix_ids` or `ip_range_ids` must be given.
- `object_type` (String) Valid values are `virtualization.vminterface` and `dcim.interface`. Required when `interface_id` is set.
- `prefix_id` (Number) Exactly one of `prefix_id` or `ip_range_id` must be given.
- `prefix_id` (Number) Exactly one of `prefix_id`, `ip_range_id`, `prefix_ids` or `ip_range_ids` must be given.
- `prefix_ids` (List(number)) Exactly one of `prefix_id`, `ip_range_id`, `prefix_ids` or `ip_range_ids` must be given.
- `role` (String) Valid values are `loopback`, `secondary`, `anycast`, `vip`, `vrrp`, `hsrp`, `glbp` and `carp`.
- `status` (String) Valid values are `active`, `reserved`, `deprecated`, `dhcp` and `slaac`. Defaults to `active`.
- `tags` (Set of String)
Expand All @@ -100,5 +130,4 @@ resource "netbox_available_ip_address" "myvm-ip" {

- `id` (String) The ID of this resource.
- `ip_address` (String)


- `selected_id` (Number) The ID of the selected range or prefix.
128 changes: 113 additions & 15 deletions netbox/resource_netbox_available_ip_address.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package netbox

import (
"fmt"
"strconv"

"github.com/fbreckle/go-netbox/netbox/client"
Expand Down Expand Up @@ -35,12 +36,33 @@ This resource will retrieve the next available IP address from a given prefix or
"prefix_id": {
Type: schema.TypeInt,
Optional: true,
ExactlyOneOf: []string{"prefix_id", "ip_range_id"},
ExactlyOneOf: []string{"prefix_id", "ip_range_id", "prefix_ids", "ip_range_ids"},
},
"prefix_ids": {
Type: schema.TypeList,
Optional: true,
ExactlyOneOf: []string{"prefix_id", "ip_range_id", "prefix_ids", "ip_range_ids"},
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
"ip_range_id": {
Type: schema.TypeInt,
Optional: true,
ExactlyOneOf: []string{"prefix_id", "ip_range_id"},
ExactlyOneOf: []string{"prefix_id", "ip_range_id", "prefix_ids", "ip_range_ids"},
},
"ip_range_ids": {
Type: schema.TypeList,
Optional: true,
ExactlyOneOf: []string{"prefix_id", "ip_range_id", "prefix_ids", "ip_range_ids"},
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
"selected_id": {
Type: schema.TypeInt,
Computed: true,
Description: "The ID of the prefix or IP range that was used to generate the IP address.",
},
"ip_address": {
Type: schema.TypeString,
Expand Down Expand Up @@ -105,34 +127,110 @@ This resource will retrieve the next available IP address from a given prefix or
}
}

type ipamIPAvailableIpsCreateCreated interface {
GetPayload() []*models.IPAddress
}

func payloadHandlerCreate(res ipamIPAvailableIpsCreateCreated) (int64, string, error) {
if res == nil {
// Ranges causes issues here.
return 0, "", fmt.Errorf("payload is nil, this could be caused by providing the wrong ID")
}
if len(res.GetPayload()) != 1 {
return 0, "", fmt.Errorf("expected 1 ip address, got %d", len(res.GetPayload()))
}
return res.GetPayload()[0].ID, *res.GetPayload()[0].Address, nil
}

func resourceNetboxAvailableIPAddressCreate(d *schema.ResourceData, m interface{}) error {
api := m.(*client.NetBoxAPI)
prefixID := int64(d.Get("prefix_id").(int))
vrfID := int64(int64(d.Get("vrf_id").(int)))
rangeID := int64(d.Get("ip_range_id").(int))
var selectedID int64
var err error

vrfID := int64(d.Get("vrf_id").(int))
nestedvrf := models.NestedVRF{
ID: vrfID,
}
data := models.AvailableIP{
Vrf: &nestedvrf,
}
if prefixID != 0 {

var res ipamIPAvailableIpsCreateCreated
if prefixID := int64(d.Get("prefix_id").(int)); prefixID != 0 {
params := ipam.NewIpamPrefixesAvailableIpsCreateParams().WithID(prefixID).WithData([]*models.AvailableIP{&data})
res, _ := api.Ipam.IpamPrefixesAvailableIpsCreate(params, nil)
// Since we generated the ip_address, set that now
d.SetId(strconv.FormatInt(res.Payload[0].ID, 10))
d.Set("ip_address", *res.Payload[0].Address)
res, err = api.Ipam.IpamPrefixesAvailableIpsCreate(params, nil)
if err != nil {
return fmt.Errorf("unable to create a ip address for prefix: %v, err: %w", prefixID, err)
}
selectedID = prefixID
}
if rangeID != 0 {
if rangeID := int64(d.Get("ip_range_id").(int)); rangeID != 0 {
selectedID = rangeID
params := ipam.NewIpamIPRangesAvailableIpsCreateParams().WithID(rangeID).WithData([]*models.AvailableIP{&data})
res, _ := api.Ipam.IpamIPRangesAvailableIpsCreate(params, nil)
// Since we generated the ip_address, set that now
d.SetId(strconv.FormatInt(res.Payload[0].ID, 10))
d.Set("ip_address", *res.Payload[0].Address)
res, err = api.Ipam.IpamIPRangesAvailableIpsCreate(params, nil)
if err != nil {
return fmt.Errorf("unable to create a ip address for ip Range: %v, err: %w", rangeID, err)
}
}
if prefixIDs, err := assertInterfaceToInt64Slice(d.Get("prefix_ids")); err == nil && len(prefixIDs) > 0 {
for _, selectedID := range prefixIDs {
// q: Ask for forgivnes or check first?
params := ipam.NewIpamPrefixesAvailableIpsCreateParams().WithID(selectedID).WithData([]*models.AvailableIP{&data})
res, err = api.Ipam.IpamPrefixesAvailableIpsCreate(params, nil)
if err == nil {
// There is avalible ips
break
}
}
if err != nil {
return fmt.Errorf("unable to create a ip address for prefixes: %v, err: %w", prefixIDs, err)
}
} else if err != nil {
return fmt.Errorf("unable to convert prefixIDs to []int64: %w", err)
}
if rangeIDs, err := assertInterfaceToInt64Slice(d.Get("ip_range_ids")); err == nil && len(rangeIDs) > 0 {
// Try Ranges until one does not return an error
for _, selectedID = range rangeIDs {
params := ipam.NewIpamIPRangesAvailableIpsCreateParams().WithID(selectedID).WithData([]*models.AvailableIP{&data})
res, err = api.Ipam.IpamIPRangesAvailableIpsCreate(params, nil)
if err == nil {
// There is avalible ips
break
}
}
if err != nil {
return fmt.Errorf("unable to create a ip address for ip Ranges: %v, err: %w", rangeIDs, err)
}
} else if err != nil {
return fmt.Errorf("unable to convert rangeIDs to []int64: %w", err)
}
netboxID, ipaddress, err := payloadHandlerCreate(res)
if err != nil {
return fmt.Errorf("unable to handle payload: %w", err)
}
d.SetId(strconv.FormatInt(netboxID, 10))
d.Set("ip_address", ipaddress)
d.Set("selected_id", selectedID)
return resourceNetboxAvailableIPAddressUpdate(d, m)
}

func assertInterfaceToInt64Slice(x interface{}) ([]int64, error) {
var intSlice []int64
var number int
var ok bool
xSlice, ok := x.([]interface{})
if !ok {
return nil, fmt.Errorf("assertInterfaceToInt64Slice: Unable to convert x:%v to []interface{}", x)
}
for _, v := range xSlice {
if number, ok = v.(int); !ok {
return nil, fmt.Errorf("assertSliceInterfaceToInt64: Unable to convert number:%v to int", v)
}
intSlice = append(intSlice, int64(number))
}
return intSlice, nil
}

func resourceNetboxAvailableIPAddressRead(d *schema.ResourceData, m interface{}) error {
api := m.(*client.NetBoxAPI)
id, _ := strconv.ParseInt(d.Id(), 10, 64)
Expand Down
Loading