Skip to content

Commit

Permalink
Add resource to manage virtual disks (#558)
Browse files Browse the repository at this point in the history
* feat(netbox_virtual_machine): make disk_size_gb computed

Since the addition of virtual disks, this field represents the aggregate
size of all disks. When adding virtual disks, netbox automatically
updates this field, so it must be a `computed` field to let terraform
know that there can be changes from outside of terraform.

* chore(go.mod): upgrade github.com/fbreckle/go-netbox to v0.0.0-20240229093931-1ddc00b277c1

* feat(netbox_virtual_disk): add new resource

Add support to manage [virtual disks][0], which were added in netbox
3.7.0.

[0]:https://docs.netbox.dev/en/stable/models/virtualization/virtualdisk/
  • Loading branch information
Ikke authored Mar 4, 2024
1 parent b4cbb20 commit 807b55c
Show file tree
Hide file tree
Showing 9 changed files with 368 additions and 2 deletions.
56 changes: 56 additions & 0 deletions docs/resources/virtual_disk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
# generated by https://github.com/fbreckle/terraform-plugin-docs
page_title: "netbox_virtual_disk Resource - terraform-provider-netbox"
subcategory: "Virtualization"
description: |-
From the official documentation https://docs.netbox.dev/en/stable/models/virtualization/virtualdisk/:
> A virtual disk is used to model discrete virtual hard disks assigned to virtual machines.
---

# netbox_virtual_disk (Resource)

From the [official documentation](https://docs.netbox.dev/en/stable/models/virtualization/virtualdisk/):

> A virtual disk is used to model discrete virtual hard disks assigned to virtual machines.

## Example Usage

```terraform
// Assumes vmw-cluster-01 exists in Netbox
data "netbox_cluster" "vmw_cluster_01" {
name = "vmw-cluster-01"
}
resource "netbox_virtual_machine" "base_vm" {
cluster_id = data.netbox_cluster.vmw_cluster_01.id
name = "myvm-1"
}
resource "netbox_virtual_disk" "example" {
name = "disk-01"
description = "Main disk"
size = 50
virtual_machine_id = netbox_virtual_machine.base_vm.id
}
```

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

### Required

- `name` (String)
- `size` (Number)
- `virtual_machine_id` (Number)

### Optional

- `custom_fields` (Map of String)
- `description` (String)
- `tags` (Set of String)

### Read-Only

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


16 changes: 16 additions & 0 deletions examples/resources/netbox_virtual_disk/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Assumes vmw-cluster-01 exists in Netbox
data "netbox_cluster" "vmw_cluster_01" {
name = "vmw-cluster-01"
}

resource "netbox_virtual_machine" "base_vm" {
cluster_id = data.netbox_cluster.vmw_cluster_01.id
name = "myvm-1"
}

resource "netbox_virtual_disk" "example" {
name = "disk-01"
description = "Main disk"
size = 50
virtual_machine_id = netbox_virtual_machine.base_vm.id
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21
toolchain go1.21.3

require (
github.com/fbreckle/go-netbox v0.0.0-20240216144714-1060d37403a2
github.com/fbreckle/go-netbox v0.0.0-20240229093931-1ddc00b277c1
github.com/fbreckle/terraform-plugin-docs v0.0.0-20220812121758-a828466500d3
github.com/go-openapi/runtime v0.27.1
github.com/go-openapi/strfmt v0.22.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fbreckle/go-netbox v0.0.0-20240216144714-1060d37403a2 h1:8NCjFRDamuUYQvX/JmSkp2JTx0pW5EZMHQTjPdgIhS8=
github.com/fbreckle/go-netbox v0.0.0-20240216144714-1060d37403a2/go.mod h1:3U3/m/hna9Ntd3sbHBYwZ1IqbP2+coRzoXw3mCfu3kM=
github.com/fbreckle/go-netbox v0.0.0-20240229093931-1ddc00b277c1 h1:xyVf34wJpiY4PCDlZau6hzwtbIUc1pHdaI/2B+c4+FI=
github.com/fbreckle/go-netbox v0.0.0-20240229093931-1ddc00b277c1/go.mod h1:3U3/m/hna9Ntd3sbHBYwZ1IqbP2+coRzoXw3mCfu3kM=
github.com/fbreckle/terraform-plugin-docs v0.0.0-20220812121758-a828466500d3 h1:DMSpM0btVedE2Tt1vfDHWQhf2obzjAe1F0/j8/CyfW4=
github.com/fbreckle/terraform-plugin-docs v0.0.0-20220812121758-a828466500d3/go.mod h1:j3HmJySEjx6hOAOPDjGzmzpVNDQq9SNnnF+Vm22d2rs=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
Expand Down
1 change: 1 addition & 0 deletions netbox/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func Provider() *schema.Provider {
"netbox_webhook": resourceNetboxWebhook(),
"netbox_custom_field_choice_set": resourceNetboxCustomFieldChoiceSet(),
"netbox_virtual_chassis": resourceNetboxVirtualChassis(),
"netbox_virtual_disk": resourceNetboxVirtualDisks(),
"netbox_event_rule": resourceNetboxEventRule(),
"netbox_vpn_tunnel_group": resourceNetboxVpnTunnelGroup(),
"netbox_vpn_tunnel": resourceNetboxVpnTunnel(),
Expand Down
186 changes: 186 additions & 0 deletions netbox/resource_netbox_virtual_disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package netbox

import (
"context"
"strconv"

"github.com/fbreckle/go-netbox/netbox/client"
"github.com/fbreckle/go-netbox/netbox/client/virtualization"
"github.com/fbreckle/go-netbox/netbox/models"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceNetboxVirtualDisks() *schema.Resource {
return &schema.Resource{
CreateContext: resourceNetboxVirtualDisksCreate,
ReadContext: resourceNetboxVirtualDisksRead,
UpdateContext: resourceNetboxVirtualDisksUpdate,
DeleteContext: resourceNetboxVirtualDisksDelete,
Description: `:meta:subcategory:Virtualization:From the [official documentation](https://docs.netbox.dev/en/stable/models/virtualization/virtualdisk/):
> A virtual disk is used to model discrete virtual hard disks assigned to virtual machines.`,

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
},
"size": {
Type: schema.TypeInt,
Required: true,
},
"virtual_machine_id": {
Type: schema.TypeInt,
Required: true,
},
tagsKey: tagsSchema,
customFieldsKey: customFieldsSchema,
},
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
}
}

func resourceNetboxVirtualDisksCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
api := m.(*client.NetBoxAPI)

name := d.Get("name").(string)
size := d.Get("size").(int)
virtualMachineID := d.Get("virtual_machine_id").(int)

data := models.WritableVirtualDisk{
Name: &name,
Size: int64ToPtr(int64(size)),
VirtualMachine: int64ToPtr(int64(virtualMachineID)),
}

descriptionValue, ok := d.GetOk("description")
if ok {
description := descriptionValue.(string)
data.Description = description
}

ct, ok := d.GetOk(customFieldsKey)
if ok {
data.CustomFields = ct
}

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

params := virtualization.NewVirtualizationVirtualDisksCreateParams().WithData(&data)

res, err := api.Virtualization.VirtualizationVirtualDisksCreate(params, nil)
if err != nil {
return diag.FromErr(err)
}

d.SetId(strconv.FormatInt(res.GetPayload().ID, 10))

return resourceNetboxVirtualDisksRead(ctx, d, m)
}

func resourceNetboxVirtualDisksRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
api := m.(*client.NetBoxAPI)

id, _ := strconv.ParseInt(d.Id(), 10, 64)

params := virtualization.NewVirtualizationVirtualDisksReadParams().WithID(id)

res, err := api.Virtualization.VirtualizationVirtualDisksRead(params, nil)
if err != nil {
if errresp, ok := err.(*virtualization.VirtualizationVirtualDisksReadDefault); ok {
errorcode := errresp.Code()
if errorcode == 404 {
d.SetId("")
return nil
}
}
return diag.FromErr(err)
}

VirtualDisks := res.GetPayload()

d.Set("name", VirtualDisks.Name)
d.Set("description", VirtualDisks.Description)

if VirtualDisks.Size != nil {
d.Set("size", *VirtualDisks.Size)
}
if VirtualDisks.VirtualMachine != nil {
d.Set("virtual_machine_id", VirtualDisks.VirtualMachine.ID)
}

cf := getCustomFields(res.GetPayload().CustomFields)
if cf != nil {
d.Set(customFieldsKey, cf)
}

d.Set(tagsKey, getTagListFromNestedTagList(VirtualDisks.Tags))
return nil
}

func resourceNetboxVirtualDisksUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
api := m.(*client.NetBoxAPI)

id, _ := strconv.ParseInt(d.Id(), 10, 64)
data := models.WritableVirtualDisk{}

name := d.Get("name").(string)
size := int64(d.Get("size").(int))
virtualMachineID := int64(d.Get("virtual_machine_id").(int))

data.Name = &name
data.Size = &size
data.VirtualMachine = &virtualMachineID

ct, ok := d.GetOk(customFieldsKey)
if ok {
data.CustomFields = ct
}

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

if d.HasChanges("description") {
// check if description is set
if descriptionValue, ok := d.GetOk("description"); ok {
data.Description = descriptionValue.(string)
} else {
data.Description = " "
}
}

params := virtualization.NewVirtualizationVirtualDisksUpdateParams().WithID(id).WithData(&data)

_, err := api.Virtualization.VirtualizationVirtualDisksUpdate(params, nil)
if err != nil {
return diag.FromErr(err)
}

return resourceNetboxVirtualDisksRead(ctx, d, m)
}

func resourceNetboxVirtualDisksDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
api := m.(*client.NetBoxAPI)

id, _ := strconv.ParseInt(d.Id(), 10, 64)
params := virtualization.NewVirtualizationVirtualDisksDeleteParams().WithID(id)

_, err := api.Virtualization.VirtualizationVirtualDisksDelete(params, nil)
if err != nil {
if errresp, ok := err.(*virtualization.VirtualizationVirtualDisksDeleteDefault); ok {
if errresp.Code() == 404 {
d.SetId("")
return nil
}
}
return diag.FromErr(err)
}

return nil
}
105 changes: 105 additions & 0 deletions netbox/resource_netbox_virtual_disk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package netbox

import (
"fmt"
"strconv"
"testing"

"github.com/fbreckle/go-netbox/netbox/client"
"github.com/fbreckle/go-netbox/netbox/client/virtualization"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccNetboxVirtualDisk_basic(t *testing.T) {
testSlug := "virtual_disk"
testName := testAccGetTestName(testSlug)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVirtualDiskDestroy,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "netbox_tag" "tag_a" {
name = "[%[1]s_a]"
color_hex = "123456"
}
resource "netbox_site" "test" {
name = "%[1]s"
status = "active"
}
resource "netbox_virtual_machine" "test" {
name = "%[1]s"
site_id = netbox_site.test.id
}
resource "netbox_virtual_disk" "test" {
name = "%[1]s"
description = "description"
size = 30
virtual_machine_id = netbox_virtual_machine.test.id
tags = [netbox_tag.tag_a.name]
}
`, testName),
},
{
Config: fmt.Sprintf(`
resource "netbox_tag" "tag_a" {
name = "[%[1]s_a]"
color_hex = "123456"
}
resource "netbox_site" "test" {
name = "%[1]s"
status = "active"
}
resource "netbox_virtual_machine" "test" {
name = "%[1]s"
site_id = netbox_site.test.id
}
resource "netbox_virtual_disk" "test" {
name = "%[1]s_updated"
description = "description updated"
size = 60
virtual_machine_id = netbox_virtual_machine.test.id
tags = [netbox_tag.tag_a.name]
}
`, testName),
},
{
ResourceName: "netbox_virtual_disk.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccCheckVirtualDiskDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*client.NetBoxAPI)

// loop through the resources in state, verifying each virtual machine
// is destroyed
for _, rs := range s.RootModule().Resources {
if rs.Type != "netbox_virtual_disk" {
continue
}

stateID, _ := strconv.ParseInt(rs.Primary.ID, 10, 64)
params := virtualization.NewVirtualizationVirtualDisksReadParams().WithID(stateID)
_, err := conn.Virtualization.VirtualizationVirtualDisksRead(params, nil)

if err == nil {
return fmt.Errorf("virtual disk (%s) still exists", rs.Primary.ID)
}

if errresp, ok := err.(*virtualization.VirtualizationVirtualDisksReadDefault); ok {
errorcode := errresp.Code()
if errorcode == 404 {
return nil
}
}
return err
}
return nil
}
Loading

0 comments on commit 807b55c

Please sign in to comment.