Skip to content

Commit

Permalink
Add device resource and data source
Browse files Browse the repository at this point in the history
  • Loading branch information
danischm committed Sep 18, 2024
1 parent 088a22d commit de571b6
Show file tree
Hide file tree
Showing 13 changed files with 1,024 additions and 6 deletions.
40 changes: 40 additions & 0 deletions docs/data-sources/device.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "meraki_device Data Source - terraform-provider-meraki"
subcategory: "Devices"
description: |-
This data source can read the Device configuration.
---

# meraki_device (Data Source)

This data source can read the `Device` configuration.

## Example Usage

```terraform
data "meraki_device" "example" {
id = "12345678"
serial = "1234-ABCD-1234"
}
```

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

### Required

- `serial` (String) Switch serial

### Read-Only

- `address` (String) The address of a device
- `floor_plan_id` (String) The floor plan to associate to this device. null disassociates the device from the floorplan.
- `id` (String) The id of the object
- `lat` (Number) The latitude of a device
- `lng` (Number) The longitude of a device
- `move_map_marker` (Boolean) Whether or not to set the latitude and longitude of a device based on the new address. Only applies when lat and lng are not specified.
- `name` (String) The name of a device
- `notes` (String) The notes for the device. String. Limited to 255 characters.
- `switch_profile_id` (String) The ID of a switch template to bind to the device (for available switch templates, see the `Switch Templates` endpoint). Use null to unbind the switch device from the current profile. For a device to be bindable to a switch template, it must (1) be a switch, and (2) belong to a network that is bound to a configuration template.
- `tags` (List of String) The list of tags of a device
56 changes: 56 additions & 0 deletions docs/resources/device.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "meraki_device Resource - terraform-provider-meraki"
subcategory: "Devices"
description: |-
This resource can manage the Device configuration.
---

# meraki_device (Resource)

This resource can manage the `Device` configuration.

## Example Usage

```terraform
resource "meraki_device" "example" {
serial = "1234-ABCD-1234"
address = "1600 Pennsylvania Ave"
lat = 37.4180951010362
lng = -122.098531723022
name = "My AP"
notes = "My AP's note"
tags = ["recently-added"]
}
```

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

### Required

- `serial` (String) Switch serial

### Optional

- `address` (String) The address of a device
- `floor_plan_id` (String) The floor plan to associate to this device. null disassociates the device from the floorplan.
- `lat` (Number) The latitude of a device
- `lng` (Number) The longitude of a device
- `move_map_marker` (Boolean) Whether or not to set the latitude and longitude of a device based on the new address. Only applies when lat and lng are not specified.
- `name` (String) The name of a device
- `notes` (String) The notes for the device. String. Limited to 255 characters.
- `switch_profile_id` (String) The ID of a switch template to bind to the device (for available switch templates, see the `Switch Templates` endpoint). Use null to unbind the switch device from the current profile. For a device to be bindable to a switch template, it must (1) be a switch, and (2) belong to a network that is bound to a configuration template.
- `tags` (List of String) The list of tags of a device

### Read-Only

- `id` (String) The id of the object

## Import

Import is supported using the following syntax:

```shell
terraform import meraki_device.example "<serial>,<id>"
```
4 changes: 4 additions & 0 deletions examples/data-sources/meraki_device/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
data "meraki_device" "example" {
id = "12345678"
serial = "1234-ABCD-1234"
}
1 change: 1 addition & 0 deletions examples/resources/meraki_device/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
terraform import meraki_device.example "<serial>,<id>"
9 changes: 9 additions & 0 deletions examples/resources/meraki_device/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
resource "meraki_device" "example" {
serial = "1234-ABCD-1234"
address = "1600 Pennsylvania Ave"
lat = 37.4180951010362
lng = -122.098531723022
name = "My AP"
notes = "My AP's note"
tags = ["recently-added"]
}
18 changes: 12 additions & 6 deletions gen/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ func main() {
os.Exit(1)
}

config := yamlconfig.YamlConfig{}

shortEndpointPath := ""
if endpointPath[len(endpointPath)-1] == '}' {
parts := strings.Split(endpointPath, "/")
Expand All @@ -91,19 +93,21 @@ func main() {
var schema map[string]interface{}
paths := spec.(map[string]interface{})["paths"].(map[string]interface{})
// use POST schema if it exists, otherwise fall back to PUT schema
if endpoint, ok := paths[shortEndpointPath].(map[string]interface{})["post"]; ok {
schema = endpoint.(map[string]interface{})["requestBody"].(map[string]interface{})["content"].(map[string]interface{})["application/json"].(map[string]interface{})
} else {
if sep, ok := paths[shortEndpointPath]; ok {
if endpoint, ok := sep.(map[string]interface{})["post"]; ok {
schema = endpoint.(map[string]interface{})["requestBody"].(map[string]interface{})["content"].(map[string]interface{})["application/json"].(map[string]interface{})
}
}
if schema == nil {
schema = paths[endpointPath].(map[string]interface{})["put"].(map[string]interface{})["requestBody"].(map[string]interface{})["content"].(map[string]interface{})["application/json"].(map[string]interface{})
config.PutCreate = true
}
example := schema["schema"].(map[string]interface{})["example"].(map[string]interface{})
exampleStr, err := json.Marshal(&example)
if err != nil {
panic(err)
}

config := yamlconfig.YamlConfig{}

urlResult := parseUrl(endpointPath)
if urlResult.resultPath[len(urlResult.resultPath)-1] == '/' {
urlResult.resultPath = urlResult.resultPath[:len(urlResult.resultPath)-1]
Expand Down Expand Up @@ -139,7 +143,7 @@ func main() {

dataSourceNameQuery := false
for _, a := range config.Attributes {
if a.ModelName == "name" && len(a.DataPath) == 0 {
if a.ModelName == "name" && len(a.DataPath) == 0 && !config.PutCreate {
dataSourceNameQuery = true
break
}
Expand Down Expand Up @@ -222,6 +226,8 @@ func parseUrl(url string) parseUrlResult {
ret.category = "Organizations"
} else if strings.Contains(parts[0], "/networks") {
ret.category = "Networks"
} else if strings.Contains(parts[0], "/devices") {
ret.category = "Devices"
}
if len(parts) > 0 {
if strings.Contains(parts[1], "/switch") {
Expand Down
66 changes: 66 additions & 0 deletions gen/definitions/device.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Device
rest_endpoint: /devices/%v
put_create: true
no_delete: true
doc_category: Devices
attributes:
- tf_name: serial
type: String
reference: true
description: Switch serial
example: 1234-ABCD-1234
test_value: tolist(meraki_network_device_claim.test.serials)[0]
- model_name: address
type: String
description: The address of a device
example: 1600 Pennsylvania Ave
- model_name: floorPlanId
type: String
exclude_test: true
description: The floor plan to associate to this device. null disassociates the device from the floorplan.
example: g_2176982374
- model_name: lat
type: Float64
description: The latitude of a device
example: "37.4180951010362"
- model_name: lng
type: Float64
description: The longitude of a device
example: "-122.098531723022"
- model_name: moveMapMarker
type: Bool
exclude_test: true
description: Whether or not to set the latitude and longitude of a device based on the new address. Only applies when lat and lng are not specified.
example: "true"
- model_name: name
type: String
description: The name of a device
example: My AP
minimum_test_value: '"My AP1"'
- model_name: notes
type: String
description: The notes for the device. String. Limited to 255 characters.
example: My AP's note
- model_name: switchProfileId
type: String
exclude_test: true
description: The ID of a switch template to bind to the device (for available switch templates, see the `Switch Templates` endpoint). Use null to unbind the switch device from the current profile. For a device to be bindable to a switch template, it must (1) be a switch, and (2) belong to a network that is bound to a configuration template.
example: "1234"
- model_name: tags
type: List
element_type: String
description: The list of tags of a device
example: recently-added
test_prerequisites: |
data "meraki_organization" "test" {
name = "Dev"
}
resource "meraki_network" "test" {
organization_id = data.meraki_organization.test.id
name = "Network1"
product_types = ["switch", "wireless"]
}
resource "meraki_network_device_claim" "test" {
network_id = meraki_network.test.id
serials = ["Q5KD-PCG4-HB8R"]
}
153 changes: 153 additions & 0 deletions internal/provider/data_source_meraki_device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright © 2024 Cisco Systems, Inc. and its affiliates.
// All rights reserved.
//
// Licensed under the Mozilla Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://mozilla.org/MPL/2.0/
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: MPL-2.0

package provider

// Section below is generated&owned by "gen/generator.go". //template:begin imports
import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/netascode/go-meraki"
"github.com/tidwall/gjson"
)

// End of section. //template:end imports

// Section below is generated&owned by "gen/generator.go". //template:begin model

// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &DeviceDataSource{}
_ datasource.DataSourceWithConfigure = &DeviceDataSource{}
)

func NewDeviceDataSource() datasource.DataSource {
return &DeviceDataSource{}
}

type DeviceDataSource struct {
client *meraki.Client
}

func (d *DeviceDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_device"
}

func (d *DeviceDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
// This description is used by the documentation generator and the language server.
MarkdownDescription: "This data source can read the `Device` configuration.",

Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "The id of the object",
Computed: true,
},
"serial": schema.StringAttribute{
MarkdownDescription: "Switch serial",
Required: true,
},
"address": schema.StringAttribute{
MarkdownDescription: "The address of a device",
Computed: true,
},
"floor_plan_id": schema.StringAttribute{
MarkdownDescription: "The floor plan to associate to this device. null disassociates the device from the floorplan.",
Computed: true,
},
"lat": schema.Float64Attribute{
MarkdownDescription: "The latitude of a device",
Computed: true,
},
"lng": schema.Float64Attribute{
MarkdownDescription: "The longitude of a device",
Computed: true,
},
"move_map_marker": schema.BoolAttribute{
MarkdownDescription: "Whether or not to set the latitude and longitude of a device based on the new address. Only applies when lat and lng are not specified.",
Computed: true,
},
"name": schema.StringAttribute{
MarkdownDescription: "The name of a device",
Computed: true,
},
"notes": schema.StringAttribute{
MarkdownDescription: "The notes for the device. String. Limited to 255 characters.",
Computed: true,
},
"switch_profile_id": schema.StringAttribute{
MarkdownDescription: "The ID of a switch template to bind to the device (for available switch templates, see the `Switch Templates` endpoint). Use null to unbind the switch device from the current profile. For a device to be bindable to a switch template, it must (1) be a switch, and (2) belong to a network that is bound to a configuration template.",
Computed: true,
},
"tags": schema.ListAttribute{
MarkdownDescription: "The list of tags of a device",
ElementType: types.StringType,
Computed: true,
},
},
}
}

func (d *DeviceDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

d.client = req.ProviderData.(*MerakiProviderData).Client
}

// End of section. //template:end model

// Section below is generated&owned by "gen/generator.go". //template:begin read

func (d *DeviceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config Device

// Read config
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Read", config.Id.String()))

var res gjson.Result
var err error

if !res.Exists() {
res, err = d.client.Get(config.getPath())
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object, got error: %s", err))
return
}
}

config.fromBody(ctx, res)

tflog.Debug(ctx, fmt.Sprintf("%s: Read finished successfully", config.Id.ValueString()))

diags = resp.State.Set(ctx, &config)
resp.Diagnostics.Append(diags...)
}

// End of section. //template:end read
Loading

0 comments on commit de571b6

Please sign in to comment.