Skip to content

Commit

Permalink
feat: Add custom_fields to vm resource and update documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Jochen Dick authored and smutel committed Dec 12, 2020
1 parent a9fbe37 commit 0fb146d
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 0 deletions.
16 changes: 16 additions & 0 deletions docs/resources/virtualization_vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ resource "netbox_virtualization_vm" "vm_test" {
name = "tag1"
slug = "tag1"
}
custom_fields = {
cf_boolean = "true"
cf_date = "2020-12-25"
cf_integer = "10"
cf_selection = "1"
cf_text = "Some text"
cf_url = "https://github.com"
}
}
```

Expand All @@ -36,6 +45,13 @@ The following arguments are supported:
The ``tag`` block supports:
* ``name`` - (Required) Name of the existing tag to associate with this resource.
* ``slug`` - (Required) Slug of the existing tag to associate with this resource.
* ``custom_fields`` - (Optional) Custom Field Keys and Values for this object
** For boolean, use the string value "true" or "false"
** For data, use the string format "YYYY-MM-DD"
** For integer, use the value between double quote "10"
** For selection, use the level id
** For text, use the string value
** For URL, use the URL as string

## Attributes Reference

Expand Down
35 changes: 35 additions & 0 deletions netbox/resource_netbox_virtualization_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,24 @@ func resourceNetboxVirtualizationVM() *schema.Resource {
Optional: true,
Default: 0,
},
"custom_fields": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
// terraform default behavior sees a difference between null and an empty string
// therefore we override the default, because null in terraform results in empty string in netbox
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
// function is called for each member of map
// including additional call on the amount of entries
// we ignore the count, because the actual state always returns the amount of existing custom_fields and all are optional in terraform
if k == "custom_fields.%" {
return true
}
return old == new
},
},
},
}
}
Expand All @@ -110,6 +128,8 @@ func resourceNetboxVirtualizationVMCreate(d *schema.ResourceData,
tags := d.Get("tag").(*schema.Set).List()
tenantID := int64(d.Get("tenant_id").(int))
vcpus := int64(d.Get("vcpus").(int))
resourceCustomFields := d.Get("custom_fields").(map[string]interface{})
customFields := convertCustomFieldsFromTerraformToAPICreate(resourceCustomFields)

newResource := &models.WritableVirtualMachineWithConfigContext{
Cluster: &clusterID,
Expand Down Expand Up @@ -144,6 +164,8 @@ func resourceNetboxVirtualizationVMCreate(d *schema.ResourceData,
newResource.Vcpus = &vcpus
}

newResource.CustomFields = &customFields

resource := virtualization.NewVirtualizationVirtualMachinesCreateParams().WithData(newResource)

resourceCreated, err := client.Virtualization.VirtualizationVirtualMachinesCreate(resource, nil)
Expand Down Expand Up @@ -247,6 +269,12 @@ func resourceNetboxVirtualizationVMRead(d *schema.ResourceData,
return err
}

customFields := convertCustomFieldsFromAPIToTerraform(resource.CustomFields)

if err = d.Set("custom_fields", customFields); err != nil {
return err
}

return nil
}
}
Expand Down Expand Up @@ -314,6 +342,13 @@ func resourceNetboxVirtualizationVMUpdate(d *schema.ResourceData,
params.Vcpus = &vcpus
}

//
if d.HasChange("custom_fields") {
stateCustomFields, resourceCustomFields := d.GetChange("custom_fields")
customFields := convertCustomFieldsFromTerraformToAPIUpdate(stateCustomFields, resourceCustomFields)
params.CustomFields = &customFields
}

resource := virtualization.NewVirtualizationVirtualMachinesPartialUpdateParams().WithData(params)

resourceID, err := strconv.ParseInt(d.Id(), 10, 64)
Expand Down
63 changes: 63 additions & 0 deletions netbox/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,66 @@ func updatePrimaryStatus(m interface{}, info InfosForPrimary, id int64) error {
* return customFieldsAPI
* }
*/

// custom_fields have multiple data type returns based on field type
// but terraform only supports map[string]string, so we convert all to strings
func convertCustomFieldsFromAPIToTerraform(customFields interface{}) map[string]string {
toReturn := make(map[string]string)
switch t := customFields.(type) {
case map[string]interface{}:
for key, value := range t {
var strValue string
if value != nil {
switch v := value.(type) {
default:
strValue = fmt.Sprintf("%v", v)
case map[string]interface{}:
strValue = fmt.Sprintf("%v", v["value"])
}
}
toReturn[key] = strValue

}
}

return toReturn
}

func convertCustomFieldsFromTerraformToAPICreate(customFields map[string]interface{}) map[string]interface{} {
toReturn := make(map[string]interface{})
for key, value := range customFields {
toReturn[key] = value

// special handling for booleans, as they are the only parameter not supplied as string to netbox
if value == "true" {
toReturn[key] = 1
} else if value == "false" {
toReturn[key] = 0
}
}

return toReturn
}

// all custom fields need to be submitted to the netbox API for updates
func convertCustomFieldsFromTerraformToAPIUpdate(stateCustomFields, resourceCustomFields interface{}) map[string]interface{} {
toReturn := make(map[string]interface{})

// netbox needs explicit empty string to remove old values
// first we fill all existing fields from the state with an empty string
for key := range stateCustomFields.(map[string]interface{}) {
toReturn[key] = nil
}
// then we override the values that still exist in the terraform code with their respective value
for key, value := range resourceCustomFields.(map[string]interface{}) {
toReturn[key] = value

// special handling for booleans, as they are the only parameter not supplied as string to netbox
if value == "true" {
toReturn[key] = 1
} else if value == "false" {
toReturn[key] = 0
}
}
return toReturn
}

0 comments on commit 0fb146d

Please sign in to comment.