From f7e5ab2473e8390b752765b896f21a81d2ae4359 Mon Sep 17 00:00:00 2001 From: smutel Date: Tue, 27 Oct 2020 21:38:53 +0100 Subject: [PATCH] enh: Add primary ip4 option for ip addresses --- docs/resources/ipam_ip_addresses.md | 7 +- examples/main.tf | 13 +- netbox/resource_netbox_ipam_ip_addresses.go | 167 +++++++++++++++----- netbox/util.go | 80 ++++++++++ 4 files changed, 215 insertions(+), 52 deletions(-) diff --git a/docs/resources/ipam_ip_addresses.md b/docs/resources/ipam_ip_addresses.md index 1982b09c6..3bbe1c2d8 100644 --- a/docs/resources/ipam_ip_addresses.md +++ b/docs/resources/ipam_ip_addresses.md @@ -23,11 +23,12 @@ The following arguments are supported: * ``address`` - (Required) The IP address (with mask) used for this object. * ``description`` - (Optional) The description of this object. * ``dns_name`` - (Optional) The DNS name of this object. -* ``object_id`` - (Optional) The ID of the object where this object is attached to. -* ``object_type`` - (Optional) The object type among virtualization.vminterface -or dcim.interface (virtualization.vminterface by default) * ``nat_inside_id`` - (Optional) The ID of the NAT inside of this object. * ``nat_outside_id`` - (Optional) The ID of the NAT outside of this object. +* ``object_id`` - (Optional) The ID of the object where this resource is attached to. +* ``object_type`` - (Optional) The object type among virtualization.vminterface +or dcim.interface (virtualization.vminterface by default) +* ``primary_ip4`` - (Optional) Set this resource as primary IPv4 (false by default) * ``role`` - (Optional) The role among loopback, secondary, anycast, vip, vrrp, hsrp, glbp, carp of this object. * ``status`` - (Optional) The status among container, active, reserved, deprecated (active by default). * ``tenant_id`` - (Optional) ID of the tenant where this object is attached. diff --git a/examples/main.tf b/examples/main.tf index 148971802..5c796217d 100644 --- a/examples/main.tf +++ b/examples/main.tf @@ -82,6 +82,7 @@ resource "netbox_ipam_ip_addresses" "ip_test" { tenant_id = netbox_tenancy_tenant.tenant_test.id object_id = netbox_virtualization_interface.interface_test.id object_type = netbox_virtualization_interface.interface_test.type + primary_ip4 = true tag { name = "tag1" @@ -103,13 +104,13 @@ data "netbox_dcim_platform" "platform_test" { } resource "netbox_virtualization_vm" "vm_test" { - cluster_id = data.netbox_virtualization_cluster.cluster_test.id - name = "test" - disk = 10 - memory = 10 + cluster_id = data.netbox_virtualization_cluster.cluster_test.id + name = "test" + disk = 10 + memory = 10 platform_id = data.netbox_dcim_platform.platform_test.id - tenant_id = netbox_tenancy_tenant.tenant_test.id - role_id = 1 + tenant_id = netbox_tenancy_tenant.tenant_test.id + role_id = 1 tag { name = "tag1" diff --git a/netbox/resource_netbox_ipam_ip_addresses.go b/netbox/resource_netbox_ipam_ip_addresses.go index 28d48b761..0d405b05e 100644 --- a/netbox/resource_netbox_ipam_ip_addresses.go +++ b/netbox/resource_netbox_ipam_ip_addresses.go @@ -42,6 +42,14 @@ func resourceNetboxIpamIPAddresses() *schema.Resource { regexp.MustCompile("^[-a-zA-Z0-9_.]{1,255}$"), "Must be like ^[-a-zA-Z0-9_.]{1,255}$"), }, + "nat_inside_id": { + Type: schema.TypeInt, + Optional: true, + }, + "nat_outside_id": { + Type: schema.TypeInt, + Optional: true, + }, "object_id": { Type: schema.TypeInt, Optional: true, @@ -54,13 +62,11 @@ func resourceNetboxIpamIPAddresses() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ "virtualization.vminterface", "dcim.interface"}, false), }, - "nat_inside_id": { - Type: schema.TypeInt, - Optional: true, - }, - "nat_outside_id": { - Type: schema.TypeInt, + "primary_ip4": { + Type: schema.TypeBool, Optional: true, + Default: false, + ForceNew: true, }, "role": { Type: schema.TypeString, @@ -112,10 +118,11 @@ func resourceNetboxIpamIPAddressesCreate(d *schema.ResourceData, address := d.Get("address").(string) description := d.Get("description").(string) dnsName := d.Get("dns_name").(string) - objectID := int64(d.Get("object_id").(int)) - objectType := d.Get("object_type").(string) natInsideID := int64(d.Get("nat_inside_id").(int)) natOutsideID := int64(d.Get("nat_outside_id").(int)) + objectID := int64(d.Get("object_id").(int)) + objectType := d.Get("object_type").(string) + primary_ip4 := d.Get("primary_ip4").(bool) role := d.Get("role").(string) status := d.Get("status").(string) tags := d.Get("tag").(*schema.Set).List() @@ -131,11 +138,6 @@ func resourceNetboxIpamIPAddressesCreate(d *schema.ResourceData, Tags: convertTagsToNestedTags(tags), } - if objectID != 0 { - newResource.AssignedObjectID = &objectID - newResource.AssignedObjectType = objectType - } - if natInsideID != 0 { newResource.NatInside = &natInsideID } @@ -144,6 +146,22 @@ func resourceNetboxIpamIPAddressesCreate(d *schema.ResourceData, newResource.NatOutside = &natOutsideID } + var info InfosForPrimary + if primary_ip4 && objectID != 0 { + if objectType == "virtualization.vminterface" { + var err error + info, err = getInfoForPrimary(m, objectID) + if err != nil { + return err + } + } + } + + if objectID != 0 { + newResource.AssignedObjectID = &objectID + newResource.AssignedObjectType = objectType + } + if tenantID != 0 { newResource.Tenant = &tenantID } @@ -161,6 +179,11 @@ func resourceNetboxIpamIPAddressesCreate(d *schema.ResourceData, d.SetId(strconv.FormatInt(resourceCreated.Payload.ID, 10)) + err = updatePrimaryStatus(client, info, resourceCreated.Payload.ID) + if err != nil { + return err + } + return resourceNetboxIpamIPAddressesRead(d, m) } @@ -196,42 +219,67 @@ func resourceNetboxIpamIPAddressesRead(d *schema.ResourceData, return err } - if resource.AssignedObjectID == nil { - if err = d.Set("object_id", nil); err != nil { + if resource.NatInside == nil { + if err = d.Set("nat_inside_id", nil); err != nil { return err } } else { - if err = d.Set("object_id", resource.AssignedObjectID); err != nil { + if err = d.Set("nat_inside_id", resource.NatInside.ID); err != nil { return err } } - objectType := resource.AssignedObjectType - if objectType == "" { - objectType = "virtualization.vminterface" - } - if err = d.Set("object_type", objectType); err != nil { - return err - } - - if resource.NatInside == nil { - if err = d.Set("nat_inside_id", nil); err != nil { + if resource.NatOutside == nil { + if err = d.Set("nat_outside_id", nil); err != nil { return err } } else { - if err = d.Set("nat_inside_id", resource.NatInside.ID); err != nil { + if err = d.Set("nat_outside_id", resource.NatOutside.ID); err != nil { return err } } - if resource.NatOutside == nil { - if err = d.Set("nat_outside_id", nil); err != nil { + if resource.AssignedObjectID == nil { + if err = d.Set("object_id", nil); err != nil { + return err + } + + if err = d.Set("primary_ip4", false); err != nil { return err } } else { - if err = d.Set("nat_outside_id", resource.NatOutside.ID); err != nil { + if err = d.Set("object_id", resource.AssignedObjectID); err != nil { return err } + + var info InfosForPrimary + if *resource.AssignedObjectID != 0 { + if resource.AssignedObjectType == "virtualization.vminterface" { + var err error + info, err = getInfoForPrimary(m, *resource.AssignedObjectID) + if err != nil { + return err + } + + if info.vm_primary_ip4_id == resource.ID { + if err = d.Set("primary_ip4", true); err != nil { + return err + } + } else { + if err = d.Set("primary_ip4", false); err != nil { + return err + } + } + } + } + } + + objectType := resource.AssignedObjectType + if objectType == "" { + objectType = "virtualization.vminterface" + } + if err = d.Set("object_type", objectType); err != nil { + return err } if resource.Role == nil { @@ -291,6 +339,7 @@ func resourceNetboxIpamIPAddressesUpdate(d *schema.ResourceData, m interface{}) error { client := m.(*netboxclient.NetBoxAPI) params := &models.WritableIPAddress{} + // primary_ip4 := false // Required parameters address := d.Get("address").(string) @@ -308,19 +357,6 @@ func resourceNetboxIpamIPAddressesUpdate(d *schema.ResourceData, params.DNSName = d.Get("dns_name").(string) } - if d.HasChange("object_id") || d.HasChange("object_type") { - objectID := int64(d.Get("object_id").(int)) - params.AssignedObjectID = &objectID - - var objectType string - if params.AssignedObjectType == "" { - objectType = "virtualization.vminterface" - } else { - objectType = d.Get("object_type").(string) - } - params.AssignedObjectType = objectType - } - if d.HasChange("nat_inside_id") { natInsideID := int64(d.Get("nat_inside_id").(int)) if natInsideID != 0 { @@ -335,6 +371,20 @@ func resourceNetboxIpamIPAddressesUpdate(d *schema.ResourceData, } } + if d.HasChange("object_id") || d.HasChange("object_type") { + // primary_ip4 = true + objectID := int64(d.Get("object_id").(int)) + params.AssignedObjectID = &objectID + + var objectType string + if params.AssignedObjectType == "" { + objectType = "virtualization.vminterface" + } else { + objectType = d.Get("object_type").(string) + } + params.AssignedObjectType = objectType + } + if d.HasChange("role") { role := d.Get("role").(string) params.Role = role @@ -377,6 +427,37 @@ func resourceNetboxIpamIPAddressesUpdate(d *schema.ResourceData, return err } + /* + * if primary_ip4 || d.HasChange("primary_ip4") { + * var info InfosForPrimary + * objectID := int64(d.Get("object_id").(int)) + * objectType := d.Get("object_type").(string) + * isPrimary := d.Get("primary_ip4").(bool) + * if objectID != 0 { + * if objectType == "virtualization.vminterface" { + * var err error + * info, err = getInfoForPrimary(m, objectID) + * if err != nil { + * return err + * } + * } + * } + * + * var ipID int64 + * ipID = 0 + * if isPrimary { + * ipID, err = strconv.ParseInt(d.Id(), 10, 64) + * if err != nil { + * return fmt.Errorf("Unable to convert ID into int64") + * } + * } + * err = updatePrimaryStatus(client, info, ipID) + * if err != nil { + * return err + * } + * } + */ + return resourceNetboxIpamIPAddressesRead(d, m) } diff --git a/netbox/util.go b/netbox/util.go index a86187c69..09b6f1716 100644 --- a/netbox/util.go +++ b/netbox/util.go @@ -1,9 +1,20 @@ package netbox import ( + "fmt" + + netboxclient "github.com/netbox-community/go-netbox/netbox/client" + "github.com/netbox-community/go-netbox/netbox/client/virtualization" "github.com/netbox-community/go-netbox/netbox/models" ) +type InfosForPrimary struct { + vm_id int64 + vm_name string + vm_cluster_id int64 + vm_primary_ip4_id int64 +} + func expandToStringSlice(v []interface{}) []string { s := make([]string, len(v)) for i, val := range v { @@ -56,6 +67,75 @@ func convertNestedTagsToTags(tags []*models.NestedTag) []map[string]string { return tfTags } +func getInfoForPrimary(m interface{}, objectID int64) (InfosForPrimary, error) { + client := m.(*netboxclient.NetBoxAPI) + + objectIDStr := fmt.Sprintf("%d", objectID) + info := InfosForPrimary{} + paramsInterface := virtualization.NewVirtualizationInterfacesListParams().WithID( + &objectIDStr) + interfaces, err := client.Virtualization.VirtualizationInterfacesList( + paramsInterface, nil) + + if err != nil { + return info, err + } + + for _, i := range interfaces.Payload.Results { + if i.ID == objectID { + if i.VirtualMachine != nil { + vm_id_str := fmt.Sprintf("%d", i.VirtualMachine.ID) + paramsVm := virtualization.NewVirtualizationVirtualMachinesListParams().WithID(&vm_id_str) + vms, err := client.Virtualization.VirtualizationVirtualMachinesList(paramsVm, nil) + + if err != nil { + return info, err + } + + if *vms.Payload.Count != 1 { + return info, fmt.Errorf("Cannot set an interface as primary when the " + + "interface is not attached to a VM.") + } + + info.vm_id = i.VirtualMachine.ID + info.vm_name = *vms.Payload.Results[0].Name + info.vm_cluster_id = vms.Payload.Results[0].Cluster.ID + + if vms.Payload.Results[0].PrimaryIp4 != nil { + info.vm_primary_ip4_id = vms.Payload.Results[0].PrimaryIp4.ID + } else { + info.vm_primary_ip4_id = 0 + } + } else { + return info, fmt.Errorf("Cannot set an interface as primary when the " + + "interface is not attached to a VM.") + } + } + } + + return info, nil +} + +func updatePrimaryStatus(m interface{}, info InfosForPrimary, id int64) error { + client := m.(*netboxclient.NetBoxAPI) + + if info.vm_name != "" { + params := &models.WritableVirtualMachineWithConfigContext{} + params.Name = &info.vm_name + params.Cluster = &info.vm_cluster_id + params.PrimaryIp4 = &id + vm := virtualization.NewVirtualizationVirtualMachinesPartialUpdateParams().WithData(params) + vm.SetID(info.vm_id) + _, err := client.Virtualization.VirtualizationVirtualMachinesPartialUpdate( + vm, nil) + if err != nil { + return err + } + } + + return nil +} + /* * func diffSlices(oldSlice []string, newSlice []string) []string { * var diff []string