From 8798e24db012c96e5be73dfd98370ed4ace2f7d4 Mon Sep 17 00:00:00 2001 From: smutel Date: Sat, 24 Oct 2020 21:43:13 +0200 Subject: [PATCH] feat: Add VirtualMachineInterface resource --- examples/main.tf | 8 + netbox/provider.go | 15 +- ...esource_netbox_virtualization_interface.go | 354 ++++++++++++++++++ netbox/util.go | 11 + 4 files changed, 381 insertions(+), 7 deletions(-) create mode 100644 netbox/resource_netbox_virtualization_interface.go diff --git a/examples/main.tf b/examples/main.tf index b4f507c5f..dd6ba9b0f 100644 --- a/examples/main.tf +++ b/examples/main.tf @@ -80,6 +80,7 @@ resource "netbox_ipam_ip_addresses" "ip_test" { address = "192.168.56.1/24" status = "active" tenant_id = netbox_tenancy_tenant.tenant_test.id + object_id = netbox_virtualization_interface.interface_test.id tag { name = "tag1" @@ -106,3 +107,10 @@ resource "netbox_virtualization_vm" "vm_test" { slug = "tag1" } } + +resource "netbox_virtualization_interface" "interface_test" { + name = "default" + virtualmachine_id = netbox_virtualization_vm.vm_test.id + mac_address = "AA:AA:AA:AA:AA:AA" + description = "Interface de test" +} diff --git a/netbox/provider.go b/netbox/provider.go index 8f35744b4..a95e5fb67 100644 --- a/netbox/provider.go +++ b/netbox/provider.go @@ -45,13 +45,14 @@ func Provider() *schema.Provider { "netbox_tenancy_tenant_group": dataNetboxTenancyTenantGroup(), }, ResourcesMap: map[string]*schema.Resource{ - "netbox_ipam_prefix": resourceNetboxIpamPrefix(), - "netbox_ipam_ip_addresses": resourceNetboxIpamIPAddresses(), - "netbox_ipam_vlan": resourceNetboxIpamVlan(), - "netbox_ipam_vlan_group": resourceNetboxIpamVlanGroup(), - "netbox_tenancy_tenant": resourceNetboxTenancyTenant(), - "netbox_tenancy_tenant_group": resourceNetboxTenancyTenantGroup(), - "netbox_virtualization_vm": resourceNetboxVirtualizationVM(), + "netbox_ipam_prefix": resourceNetboxIpamPrefix(), + "netbox_ipam_ip_addresses": resourceNetboxIpamIPAddresses(), + "netbox_ipam_vlan": resourceNetboxIpamVlan(), + "netbox_ipam_vlan_group": resourceNetboxIpamVlanGroup(), + "netbox_tenancy_tenant": resourceNetboxTenancyTenant(), + "netbox_tenancy_tenant_group": resourceNetboxTenancyTenantGroup(), + "netbox_virtualization_interface": resourceNetboxVirtualizationInterface(), + "netbox_virtualization_vm": resourceNetboxVirtualizationVM(), }, ConfigureFunc: configureProvider, } diff --git a/netbox/resource_netbox_virtualization_interface.go b/netbox/resource_netbox_virtualization_interface.go new file mode 100644 index 000000000..22ade1e1f --- /dev/null +++ b/netbox/resource_netbox_virtualization_interface.go @@ -0,0 +1,354 @@ +package netbox + +import ( + "fmt" + "regexp" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + 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" +) + +const TYPE string = "virtualization.vminterface" + +func resourceNetboxVirtualizationInterface() *schema.Resource { + return &schema.Resource{ + Create: resourceNetboxVirtualizationInterfaceCreate, + Read: resourceNetboxVirtualizationInterfaceRead, + Update: resourceNetboxVirtualizationInterfaceUpdate, + Delete: resourceNetboxVirtualizationInterfaceDelete, + Exists: resourceNetboxVirtualizationInterfaceExists, + + Schema: map[string]*schema.Schema{ + "description": { + Type: schema.TypeString, + Optional: true, + Default: " ", + ValidateFunc: validation.StringLenBetween(1, 200), + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "mac_address": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile("^([A-Z0-9]{2}:){5}[A-Z0-9]{2}$"), + "Must be like AA:AA:AA:AA:AA"), + ForceNew: true, + }, + "mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"access", "tagged", + "tagged-all"}, false), + ForceNew: true, + }, + "mtu": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 65536), + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "tagged_vlans": { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + Optional: true, + }, + "tag": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "slug": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "untagged_vlan": { + Type: schema.TypeInt, + Optional: true, + }, + "virtualmachine_id": { + Type: schema.TypeInt, + Required: true, + }, + }, + } +} + +func resourceNetboxVirtualizationInterfaceCreate(d *schema.ResourceData, + m interface{}) error { + client := m.(*netboxclient.NetBoxAPI) + + description := d.Get("description").(string) + enabled := d.Get("enabled").(bool) + macAddress := d.Get("mac_address").(string) + mode := d.Get("mode").(string) + mtu := int64(d.Get("mtu").(int)) + name := d.Get("name").(string) + taggedVlans := d.Get("tagged_vlans").(*schema.Set).List() + tags := d.Get("tag").(*schema.Set).List() + untaggedVlan := int64(d.Get("untagged_vlan").(int)) + virtualmachineID := int64(d.Get("virtualmachine_id").(int)) + + newResource := &models.WritableVMInterface{ + Description: description, + Enabled: enabled, + Mode: mode, + Name: &name, + TaggedVlans: expandToInt64Slice(taggedVlans), + Tags: convertTagsToNestedTags(tags), + VirtualMachine: &virtualmachineID, + } + + if macAddress != "" { + newResource.MacAddress = &macAddress + } + + if mtu != 0 { + newResource.Mtu = &mtu + } + + if untaggedVlan != 0 { + newResource.UntaggedVlan = &untaggedVlan + } + + resource := virtualization.NewVirtualizationInterfacesCreateParams().WithData(newResource) + + resourceCreated, err := client.Virtualization.VirtualizationInterfacesCreate( + resource, nil) + + if err != nil { + return err + } + + d.SetId(strconv.FormatInt(resourceCreated.Payload.ID, 10)) + + return resourceNetboxVirtualizationInterfaceRead(d, m) +} + +func resourceNetboxVirtualizationInterfaceRead(d *schema.ResourceData, + m interface{}) error { + client := m.(*netboxclient.NetBoxAPI) + + resourceID := d.Id() + params := virtualization.NewVirtualizationInterfacesListParams().WithID( + &resourceID) + resources, err := client.Virtualization.VirtualizationInterfacesList( + params, nil) + + if err != nil { + return err + } + + for _, resource := range resources.Payload.Results { + if strconv.FormatInt(resource.ID, 10) == d.Id() { + var description string + + if resource.Description == "" { + description = " " + } else { + description = resource.Description + } + + if err = d.Set("description", description); err != nil { + return err + } + + if err = d.Set("enabled", resource.Enabled); err != nil { + return err + } + + if err = d.Set("mac_address", resource.MacAddress); err != nil { + return err + } + + if resource.Mode == nil { + if err = d.Set("mode", ""); err != nil { + return err + } + } else { + if err = d.Set("mode", resource.Mode.Value); err != nil { + return err + } + } + + if err = d.Set("mtu", resource.Mtu); err != nil { + return err + } + + if err = d.Set("name", resource.Name); err != nil { + return err + } + + if err = d.Set("tagged_vlans", resource.TaggedVlans); err != nil { + return err + } + + if err = d.Set("tag", convertNestedTagsToTags( + resource.Tags)); err != nil { + return err + } + + if err = d.Set("untagged_vlan", resource.UntaggedVlan); err != nil { + return err + } + + if resource.VirtualMachine == nil { + if err = d.Set("virtualmachine_id", 0); err != nil { + return err + } + } else { + if err = d.Set("virtualmachine_id", + resource.VirtualMachine.ID); err != nil { + return err + } + } + + d.Set("type", TYPE) + + return nil + } + } + + d.SetId("") + return nil +} + +func resourceNetboxVirtualizationInterfaceUpdate(d *schema.ResourceData, + m interface{}) error { + client := m.(*netboxclient.NetBoxAPI) + params := &models.WritableVMInterface{} + + // Required parameters + name := d.Get("name").(string) + params.Name = &name + virtualMachineID := int64(d.Get("virtualmachine_id").(int)) + params.VirtualMachine = &virtualMachineID + taggedVlans := d.Get("tagged_vlans").(*schema.Set).List() + params.TaggedVlans = expandToInt64Slice(taggedVlans) + + if d.HasChange("description") { + description := d.Get("description").(string) + params.Description = description + } + + if d.HasChange("enabled") { + enabled := d.Get("enabled").(bool) + params.Enabled = enabled + } + + if d.HasChange("mac_address") { + macAddress := d.Get("mac_address").(string) + params.MacAddress = &macAddress + } + + if d.HasChange("mode") { + mode := d.Get("mode").(string) + params.Mode = mode + } + + if d.HasChange("mtu") { + mtu := int64(d.Get("mtu").(int)) + params.Mtu = &mtu + } + + tags := d.Get("tag").(*schema.Set).List() + params.Tags = convertTagsToNestedTags(tags) + + if d.HasChange("untagged_vlan") { + untaggedVlan := int64(d.Get("untagged_vlan").(int)) + params.UntaggedVlan = &untaggedVlan + } + + resource := virtualization.NewVirtualizationInterfacesPartialUpdateParams().WithData(params) + + resourceID, err := strconv.ParseInt(d.Id(), 10, 64) + if err != nil { + return fmt.Errorf("Unable to convert ID into int64") + } + + resource.SetID(resourceID) + + _, err = client.Virtualization.VirtualizationInterfacesPartialUpdate( + resource, nil) + if err != nil { + return err + } + + return resourceNetboxVirtualizationInterfaceRead(d, m) +} + +func resourceNetboxVirtualizationInterfaceDelete(d *schema.ResourceData, + m interface{}) error { + client := m.(*netboxclient.NetBoxAPI) + + resourceExists, err := resourceNetboxVirtualizationInterfaceExists(d, m) + if err != nil { + return err + } + + if !resourceExists { + return nil + } + + id, err := strconv.ParseInt(d.Id(), 10, 64) + if err != nil { + return fmt.Errorf("Unable to convert ID into int64") + } + + p := virtualization.NewVirtualizationInterfacesDeleteParams().WithID(id) + if _, err := client.Virtualization.VirtualizationInterfacesDelete( + p, nil); err != nil { + return err + } + + return nil +} + +func resourceNetboxVirtualizationInterfaceExists(d *schema.ResourceData, + m interface{}) (b bool, + e error) { + client := m.(*netboxclient.NetBoxAPI) + resourceExist := false + + resourceID := d.Id() + params := virtualization.NewVirtualizationInterfacesListParams().WithID( + &resourceID) + resources, err := client.Virtualization.VirtualizationInterfacesList( + params, nil) + if err != nil { + return resourceExist, err + } + + for _, resource := range resources.Payload.Results { + if strconv.FormatInt(resource.ID, 10) == d.Id() { + resourceExist = true + } + } + + return resourceExist, nil +} diff --git a/netbox/util.go b/netbox/util.go index d99d455e5..a86187c69 100644 --- a/netbox/util.go +++ b/netbox/util.go @@ -15,6 +15,17 @@ func expandToStringSlice(v []interface{}) []string { return s } +func expandToInt64Slice(v []interface{}) []int64 { + s := make([]int64, len(v)) + for i, val := range v { + if strVal, ok := val.(int64); ok { + s[i] = strVal + } + } + + return s +} + func convertTagsToNestedTags(tags []interface{}) []*models.NestedTag { nestedTags := []*models.NestedTag{}