From 1c5e4e6f0192804f949bd3b8b5f8fa0bba0276ae Mon Sep 17 00:00:00 2001 From: Oliver Karstoft Date: Tue, 18 Jul 2023 10:22:16 +0200 Subject: [PATCH] Feat: Adding data source for device type + Fixing typo in model names (#36) * Adding data device type and fixing spelling mistake * Adding data device type * This should do the trick * The arrow faces the wrong way * Fixing deprecation warning * Removed uHeight and improved custom_fields schema --- .gitignore | 3 +- client/client.go | 24 +++- models/models.go | 19 ++- provider/data_device_type.go | 122 ++++++++++++++++++++ provider/provider.go | 5 +- provider/resource_availableprefixes_test.go | 2 +- 6 files changed, 166 insertions(+), 9 deletions(-) create mode 100644 provider/data_device_type.go diff --git a/.gitignore b/.gitignore index 7a5ad90..d709589 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,5 @@ override.tf.json # End of https://www.toptal.com/developers/gitignore/api/terraform -dist \ No newline at end of file +dist +dist/ diff --git a/client/client.go b/client/client.go index 1ab4dda..92d21de 100644 --- a/client/client.go +++ b/client/client.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "net/url" "regexp" @@ -41,7 +41,7 @@ func AvailablePrefixBody(d *schema.ResourceData) models.AvailablePrefixes { } // GetPrefix will return a prefix -func (client *Client) GetPrefix(newCidr string) (*models.ReponsePrefix, error) { +func (client *Client) GetPrefix(newCidr string) (*models.ResponsePrefix, error) { if newCidr == "" { return nil, fmt.Errorf("[ERROR] 'cidr_notation' is empty") } @@ -52,7 +52,7 @@ func (client *Client) GetPrefix(newCidr string) (*models.ReponsePrefix, error) { return nil, err } - var jsonData models.ReponsePrefix + var jsonData models.ResponsePrefix err = json.Unmarshal([]byte(resp), &jsonData) if err != nil { return nil, err @@ -185,7 +185,7 @@ func (client *Client) SendRequest(method string, path string, payload interface{ return "", err } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return "", err } @@ -201,3 +201,19 @@ func (client *Client) SendRequest(method string, path string, payload interface{ return strbody, nil } + +func (client *Client) GetDeviceType(id int) (*models.ResponseDeviceTypes, error) { + path := fmt.Sprintf("%s%d/", models.PathDeviceTypes, id) + resp, err := client.SendRequest("GET", path, nil, 200) + if err != nil { + return nil, err + } + + var jsonData models.ResponseDeviceTypes + err = json.Unmarshal([]byte(resp), &jsonData) + if err != nil { + return nil, err + } + + return &jsonData, nil +} diff --git a/models/models.go b/models/models.go index 9963b4c..1918c06 100644 --- a/models/models.go +++ b/models/models.go @@ -3,6 +3,7 @@ package models import "time" var PathAvailablePrefixes = "/ipam/prefixes/" +var PathDeviceTypes = "/dcim/device-types/" type ReponseAvailablePrefixes struct { ID int `json:"id"` @@ -98,7 +99,7 @@ type ResponseSites struct { } `json:"results"` } -type ReponsePrefix struct { +type ResponsePrefix struct { Count int `json:"count"` Next interface{} `json:"next"` Previous interface{} `json:"previous"` @@ -143,3 +144,19 @@ type ReponsePrefix struct { LastUpdated time.Time `json:"last_updated"` } `json:"results"` } + +type ResponseDeviceTypes struct { + ID int `json:"id"` + DisplayName string `json:"display"` + Manufacturer struct { + ID int `json:"id"` + DisplayName string `json:"display"` + Name string `json:"name"` + Slug string `json:"slug"` + } `json:"manufacturer"` + Model string `json:"model"` + Slug string `json:"slug"` + PartNumber string `json:"part_number"` + Descrption string `json:"description"` + CustomFields interface{} `json:"custom_fields"` +} diff --git a/provider/data_device_type.go b/provider/data_device_type.go new file mode 100644 index 0000000..5ecbbf1 --- /dev/null +++ b/provider/data_device_type.go @@ -0,0 +1,122 @@ +package provider + +import ( + "fmt" + "strconv" + + "github.com/BESTSELLER/terraform-provider-netbox/client" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func dataDeviceType() *schema.Resource { + return &schema.Resource{ + Read: dataDeviceTypeRead, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeInt, + Required: true, + Description: "A unique integer value identifying this device type.", + }, + "displayname": { + Type: schema.TypeString, + Computed: true, + Description: "The display name of the device type.", + }, + "manufacturer": { + Type: schema.TypeMap, + Computed: true, + Description: "The manufacturer object of the device type.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeInt, + Computed: true, + Description: "A unique integer value identifying this manufacturer.", + }, + "displayname": { + Type: schema.TypeString, + Computed: true, + Description: "The display name of the manufacturer.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the manufacturer name.", + }, + "slug": { + Type: schema.TypeString, + Computed: true, + Description: "The slug of the manufacturer.", + }, + }, + }, + }, + "model": { + Type: schema.TypeString, + Computed: true, + Description: "The model of the device type.", + }, + "slug": { + Type: schema.TypeString, + Computed: true, + Description: "The slug of the device type.", + }, + "part_number": { + Type: schema.TypeString, + Computed: true, + Description: "The part number of the device type.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of the device type.", + }, + "custom_fields": { + Type: schema.TypeMap, + Computed: true, + Description: "The custom fields of the device type.", + Default: nil, + Elem: &schema.Schema{ + Type: schema.TypeString, + Default: nil, + }, + }, + }, + } +} + +func dataDeviceTypeRead(d *schema.ResourceData, m interface{}) error { + apiClient := m.(*client.Client) + + id := d.Get("id").(int) + if id < 0 { + return fmt.Errorf("[ERROR] 'id' cannot be less than 0") + } + + resp, err := apiClient.GetDeviceType(id) + if err != nil { + return err + } + + cf := getCustomFields(resp.CustomFields) + if cf != nil { + d.Set("custom_fields", cf) + } + + d.Set("displayname", resp.DisplayName) + d.Set("manufacturer", resp.Manufacturer) + d.Set("model", resp.Model) + d.Set("slug", resp.Slug) + d.Set("part_number", resp.PartNumber) + d.Set("description", resp.Descrption) + d.SetId(strconv.Itoa(resp.ID)) + return nil +} + +func getCustomFields(cf interface{}) map[string]interface{} { + cfm, ok := cf.(map[string]interface{}) + if !ok || len(cfm) == 0 { + return nil + } + return cfm +} diff --git a/provider/provider.go b/provider/provider.go index f024eb8..ca3124d 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -26,8 +26,9 @@ func Provider() terraform.ResourceProvider { "netbox_available_prefix": resourceAvailablePrefixes(), }, DataSourcesMap: map[string]*schema.Resource{ - "netbox_prefix": dataPrefix(), - "netbox_sites": dataSites(), + "netbox_prefix": dataPrefix(), + "netbox_sites": dataSites(), + "netbox_device_type": dataDeviceType(), }, ConfigureFunc: providerConfigure, diff --git a/provider/resource_availableprefixes_test.go b/provider/resource_availableprefixes_test.go index d41998b..e2a4b70 100644 --- a/provider/resource_availableprefixes_test.go +++ b/provider/resource_availableprefixes_test.go @@ -48,7 +48,7 @@ func testAccCheckRegistryDestroy(s *terraform.State) error { func testAccCheckAvailablePrefix() string { - return fmt.Sprintf(` + return (` data "netbox_prefix" "prefix" { cidr_notation = "172.24.0.0/13"