Skip to content

Commit

Permalink
feat: add 'netbox_ip_address_assignment' resource (e-breuninger#601)
Browse files Browse the repository at this point in the history
  • Loading branch information
skoppe committed Sep 16, 2024
1 parent afe33dc commit 4d0ce77
Show file tree
Hide file tree
Showing 11 changed files with 990 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Assuming a device with the id `123` exists
resource "netbox_device_interface" "this" {
name = "eth0"
device_id = 123
type = "1000base-t"
}

resource "netbox_ip_address" "this" {
ip_address = "10.0.0.60/24"
status = "active"
}

resource "netbox_ip_address_assignment" "this" {
ip_address_id = netbox_ip_address.this.id
device_interface_id = netbox_device_interface.this.id
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Assuming a device with the id `123` exists
resource "netbox_device_interface" "this" {
name = "eth0"
device_id = 123
type = "1000base-t"
}

resource "netbox_ip_address" "this" {
ip_address = "10.0.0.60/24"
status = "active"
}

resource "netbox_ip_address_assignment" "this" {
ip_address_id = netbox_ip_address.this.id
interface_id = netbox_device_interface.this.id
object_type = "dcim.interface"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Assuming a virtual machine with the id `123` exists
resource "netbox_interface" "this" {
name = "eth0"
virtual_machine_id = 123
}

resource "netbox_ip_address" "this" {
ip_address = "10.0.0.60/24"
status = "active"
}

resource "netbox_ip_address_assignment" "this" {
ip_address_id = netbox_ip_address.this.id
interface_id = netbox_interface.this.id
object_type = "virtualization.vminterface"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Assuming a virtual machine with the id `123` exists
resource "netbox_interface" "this" {
name = "eth0"
virtual_machine_id = 123
}

resource "netbox_ip_address" "this" {
ip_address = "10.0.0.60/24"
status = "active"
}


resource "netbox_ip_address_assignment" "this" {
ip_address_id = netbox_ip_address.this.id
virtual_machine_interface_id = netbox_interface.this.id
}
1 change: 1 addition & 0 deletions netbox/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func Provider() *schema.Provider {
"netbox_tenant_group": resourceNetboxTenantGroup(),
"netbox_vrf": resourceNetboxVrf(),
"netbox_ip_address": resourceNetboxIPAddress(),
"netbox_ip_address_assignment": resourceNetboxIPAddressAssignment(),
"netbox_interface_template": resourceNetboxInterfaceTemplate(),
"netbox_interface": resourceNetboxInterface(),
"netbox_service": resourceNetboxService(),
Expand Down
60 changes: 44 additions & 16 deletions netbox/resource_netbox_available_ip_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ This resource will retrieve the next available IP address from a given prefix or
Type: schema.TypeString,
Computed: true,
},
"external_assignment": {
Type: schema.TypeBool,
Optional: true,
ConflictsWith: []string{"interface_id", "virtual_machine_interface_id", "device_interface_id"},
},
"interface_id": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -152,7 +157,10 @@ func resourceNetboxAvailableIPAddressRead(d *schema.ResourceData, m interface{})
}

ipAddress := res.GetPayload()
if ipAddress.AssignedObjectID != nil {

externallyAssigned := d.Get("external_assignment").(bool)

if !externallyAssigned && ipAddress.AssignedObjectID != nil {
vmInterfaceID := getOptionalInt(d, "virtual_machine_interface_id")
deviceInterfaceID := getOptionalInt(d, "device_interface_id")
interfaceID := getOptionalInt(d, "interface_id")
Expand Down Expand Up @@ -220,21 +228,41 @@ func resourceNetboxAvailableIPAddressUpdate(d *schema.ResourceData, m interface{
deviceInterfaceID := getOptionalInt(d, "device_interface_id")
interfaceID := getOptionalInt(d, "interface_id")

switch {
case vmInterfaceID != nil:
data.AssignedObjectType = strToPtr("virtualization.vminterface")
data.AssignedObjectID = vmInterfaceID
case deviceInterfaceID != nil:
data.AssignedObjectType = strToPtr("dcim.interface")
data.AssignedObjectID = deviceInterfaceID
// if interfaceID is given, object_type must be set as well
case interfaceID != nil:
data.AssignedObjectType = strToPtr(d.Get("object_type").(string))
data.AssignedObjectID = interfaceID
// default = ip is not linked to anything
default:
data.AssignedObjectType = strToPtr("")
data.AssignedObjectID = nil
// if assignment is done externally, we just pull the information, if any
if d.Get("external_assignment").(bool) {
params := ipam.NewIpamIPAddressesReadParams().WithID(id)

res, err := api.Ipam.IpamIPAddressesRead(params, nil)
if err != nil {
return err
}

ipAddress := res.GetPayload()

if ipAddress.AssignedObjectType != nil {
data.AssignedObjectType = ipAddress.AssignedObjectType
data.AssignedObjectID = ipAddress.AssignedObjectID
} else {
data.AssignedObjectType = strToPtr("")
data.AssignedObjectID = nil
}
} else {
switch {
case vmInterfaceID != nil:
data.AssignedObjectType = strToPtr("virtualization.vminterface")
data.AssignedObjectID = vmInterfaceID
case deviceInterfaceID != nil:
data.AssignedObjectType = strToPtr("dcim.interface")
data.AssignedObjectID = deviceInterfaceID
// if interfaceID is given, object_type must be set as well
case interfaceID != nil:
data.AssignedObjectType = strToPtr(d.Get("object_type").(string))
data.AssignedObjectID = interfaceID
// default = ip is not linked to anything
default:
data.AssignedObjectType = strToPtr("")
data.AssignedObjectID = nil
}
}

data.Tags, _ = getNestedTagListFromResourceDataSet(api, d.Get(tagsKey))
Expand Down
110 changes: 110 additions & 0 deletions netbox/resource_netbox_available_ip_address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,116 @@ resource "netbox_available_ip_address" "test" {
})
}

func TestAccNetboxAvailableIPAddress_deviceByObjectType_external(t *testing.T) {
startAddress := "1.4.7.1/24"
endAddress := "1.4.7.50/24"
testSlug := "av_ipa_dev_ot_ext"
testName := testAccGetTestName(testSlug)
resource.ParallelTest(t, resource.TestCase{
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccNetboxIPAddressFullDeviceDependencies(testName) + fmt.Sprintf(`
resource "netbox_ip_range" "test_range" {
start_address = "%s"
end_address = "%s"
}
resource "netbox_available_ip_address" "test" {
ip_range_id = netbox_ip_range.test_range.id
status = "active"
external_assignment = true
dns_name = "test_range.mydomain.local"
}
resource "netbox_ip_address_assignment" "test" {
ip_address_id = netbox_available_ip_address.test.id
object_type = "dcim.interface"
interface_id = netbox_device_interface.test.id
}`, startAddress, endAddress),
},
// we update the description, to see if the ip and assignment don't conflict
{
Config: testAccNetboxIPAddressFullDeviceDependencies(testName) + fmt.Sprintf(`
resource "netbox_ip_range" "test_range" {
start_address = "%s"
end_address = "%s"
}
resource "netbox_available_ip_address" "test" {
ip_range_id = netbox_ip_range.test_range.id
status = "active"
external_assignment = true
dns_name = "test_range.mydomain.local"
description = "update"
}
resource "netbox_ip_address_assignment" "test" {
ip_address_id = netbox_available_ip_address.test.id
object_type = "dcim.interface"
interface_id = netbox_device_interface.test.id
}`, startAddress, endAddress),
},
{
ResourceName: "netbox_available_ip_address.test",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"ip_range_id", "external_assignment", "interface_id", "object_type"},
},
},
})
}

func TestAccNetboxAvailableIPAddress_deviceByFieldName_external(t *testing.T) {
startAddress := "1.3.7.1/24"
endAddress := "1.3.7.50/24"
testSlug := "av_ipa_dev_fn_ext"
testName := testAccGetTestName(testSlug)
resource.ParallelTest(t, resource.TestCase{
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccNetboxIPAddressFullDeviceDependencies(testName) + fmt.Sprintf(`
resource "netbox_ip_range" "test_range" {
start_address = "%s"
end_address = "%s"
}
resource "netbox_available_ip_address" "test" {
ip_range_id = netbox_ip_range.test_range.id
status = "active"
external_assignment = true
dns_name = "test_range.mydomain.local"
}
resource "netbox_ip_address_assignment" "test" {
ip_address_id = netbox_available_ip_address.test.id
device_interface_id = netbox_device_interface.test.id
}`, startAddress, endAddress),
},
// we update the description, to see if the ip and assignment don't conflict
{
Config: testAccNetboxIPAddressFullDeviceDependencies(testName) + fmt.Sprintf(`
resource "netbox_ip_range" "test_range" {
start_address = "%s"
end_address = "%s"
}
resource "netbox_available_ip_address" "test" {
ip_range_id = netbox_ip_range.test_range.id
status = "active"
external_assignment = true
dns_name = "test_range.mydomain.local"
description = "update"
}
resource "netbox_ip_address_assignment" "test" {
ip_address_id = netbox_available_ip_address.test.id
device_interface_id = netbox_device_interface.test.id
}`, startAddress, endAddress),
},
{
ResourceName: "netbox_available_ip_address.test",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"ip_range_id", "external_assignment", "interface_id", "object_type"},
},
},
})
}

func init() {
resource.AddTestSweepers("netbox_available_ip_address", &resource.Sweeper{
Name: "netbox_available_ip_address",
Expand Down
60 changes: 44 additions & 16 deletions netbox/resource_netbox_ip_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ func resourceNetboxIPAddress() *schema.Resource {
Required: true,
ValidateFunc: validation.IsCIDR,
},
"external_assignment": {
Type: schema.TypeBool,
Optional: true,
ConflictsWith: []string{"interface_id", "virtual_machine_interface_id", "device_interface_id"},
},
"interface_id": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -185,7 +190,10 @@ func resourceNetboxIPAddressRead(d *schema.ResourceData, m interface{}) error {
}

ipAddress := res.GetPayload()
if ipAddress.AssignedObjectID != nil {

externallyAssigned := d.Get("external_assignment").(bool)

if !externallyAssigned && ipAddress.AssignedObjectID != nil {
vmInterfaceID := getOptionalInt(d, "virtual_machine_interface_id")
deviceInterfaceID := getOptionalInt(d, "device_interface_id")
interfaceID := getOptionalInt(d, "interface_id")
Expand Down Expand Up @@ -278,21 +286,41 @@ func resourceNetboxIPAddressUpdate(d *schema.ResourceData, m interface{}) error
deviceInterfaceID := getOptionalInt(d, "device_interface_id")
interfaceID := getOptionalInt(d, "interface_id")

switch {
case vmInterfaceID != nil:
data.AssignedObjectType = strToPtr("virtualization.vminterface")
data.AssignedObjectID = vmInterfaceID
case deviceInterfaceID != nil:
data.AssignedObjectType = strToPtr("dcim.interface")
data.AssignedObjectID = deviceInterfaceID
// if interfaceID is given, object_type must be set as well
case interfaceID != nil:
data.AssignedObjectType = strToPtr(d.Get("object_type").(string))
data.AssignedObjectID = interfaceID
// default = ip is not linked to anything
default:
data.AssignedObjectType = strToPtr("")
data.AssignedObjectID = nil
// if assignment is done externally, we just pull the information, if any
if d.Get("external_assignment").(bool) {
params := ipam.NewIpamIPAddressesReadParams().WithID(id)

res, err := api.Ipam.IpamIPAddressesRead(params, nil)
if err != nil {
return err
}

ipAddress := res.GetPayload()

if ipAddress.AssignedObjectType != nil {
data.AssignedObjectType = ipAddress.AssignedObjectType
data.AssignedObjectID = ipAddress.AssignedObjectID
} else {
data.AssignedObjectType = strToPtr("")
data.AssignedObjectID = nil
}
} else {
switch {
case vmInterfaceID != nil:
data.AssignedObjectType = strToPtr("virtualization.vminterface")
data.AssignedObjectID = vmInterfaceID
case deviceInterfaceID != nil:
data.AssignedObjectType = strToPtr("dcim.interface")
data.AssignedObjectID = deviceInterfaceID
// if interfaceID is given, object_type must be set as well
case interfaceID != nil:
data.AssignedObjectType = strToPtr(d.Get("object_type").(string))
data.AssignedObjectID = interfaceID
// default = ip is not linked to anything
default:
data.AssignedObjectType = strToPtr("")
data.AssignedObjectID = nil
}
}

data.Tags, _ = getNestedTagListFromResourceDataSet(api, d.Get(tagsKey))
Expand Down
Loading

0 comments on commit 4d0ce77

Please sign in to comment.