Skip to content

Commit 2f2bb84

Browse files
Added Support for Device create (#22)
* 1) Added Support for Device create 2) After Create / Update, get config and publish latest state * 1) Added Support for Device create 2) After Create / Update, get config and publish latest state
1 parent 599ac84 commit 2f2bb84

File tree

4 files changed

+155
-43
lines changed

4 files changed

+155
-43
lines changed

provider/data_source_edgenode.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ package provider
55

66
import (
77
"context"
8+
"fmt"
89
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
910
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1011
zschemas "github.com/zededa/terraform-provider-zedcloud/schemas"
12+
zedcloudapi "github.com/zededa/zedcloud-api"
1113
"github.com/zededa/zedcloud-api/swagger_models"
12-
"log"
1314
)
1415

1516
var EdgeNodeDataSourceSchema = &schema.Resource{
@@ -24,6 +25,28 @@ func getEdgeNodeDataSourceSchema() *schema.Resource {
2425
return EdgeNodeDataSourceSchema
2526
}
2627

28+
func getEdgeNode(client *zedcloudapi.Client,
29+
name, id string) (*swagger_models.DeviceConfig, error) {
30+
rspData := &swagger_models.DeviceConfig{}
31+
err := client.GetObj(deviceUrlExtension, name, id, false, rspData)
32+
if err != nil {
33+
return nil, fmt.Errorf("[ERROR] FAILED to get EdgeNode %s ( id: %s). Err: %s",
34+
name, id, err.Error())
35+
}
36+
return rspData, nil
37+
}
38+
39+
func getEdgeNodeAndPublishData(client *zedcloudapi.Client, d *schema.ResourceData,
40+
name, id string, resource bool) error {
41+
cfg, err := getEdgeNode(client, name, id)
42+
if err != nil {
43+
return fmt.Errorf("[ERROR] EdgeNode %s (id: %s) not found. Err: %s",
44+
name, id, err.Error())
45+
}
46+
marshalData(d, flattenDeviceConfig(cfg, resource))
47+
return nil
48+
}
49+
2750
func flattenGeoLocation(entry interface{}) []interface{} {
2851
loc := entry.(*swagger_models.GeoLocation)
2952
if loc == nil {
@@ -84,12 +107,9 @@ func flattenDeviceConfig(cfg *swagger_models.DeviceConfig, computedOnly bool) ma
84107
"cpu": int(cfg.CPU),
85108
"id": cfg.ID,
86109
"memory": int(cfg.Memory),
87-
"model_id": ptrValStr(cfg.ModelID),
88-
"project_id": ptrValStr(cfg.ProjectID),
89110
"reset_counter": int(cfg.ResetCounter),
90111
"reset_time": cfg.ResetTime,
91112
"revision": flattenObjectRevision(cfg.Revision),
92-
"serialno": cfg.Serialno,
93113
"storage": int(cfg.Storage),
94114
"thread": int(cfg.Thread),
95115
"utype": ptrValStr(cfg.Utype),
@@ -102,8 +122,10 @@ func flattenDeviceConfig(cfg *swagger_models.DeviceConfig, computedOnly bool) ma
102122
data["dev_location"] = flattenGeoLocation(cfg.DevLocation)
103123
data["eve_image_version"] = eveImageVersion
104124
data["interface"] = flattenSysInterfaces(cfg.Interfaces)
125+
data["model_id"] = ptrValStr(cfg.ModelID)
105126
data["name"] = ptrValStr(cfg.Name)
106127
data["project_id"] = ptrValStr(cfg.ProjectID)
128+
data["serialno"] = cfg.Serialno
107129
data["tags"] = flattenStringMap(cfg.Tags)
108130
data["title"] = ptrValStr(cfg.Title)
109131
}
@@ -127,14 +149,10 @@ func readEdgeNode(ctx context.Context, d *schema.ResourceData,
127149
if (id == "") && (name == "") {
128150
return diag.Errorf("The arguments \"id\" or \"name\" are required, but no definition was found.")
129151
}
130-
cfg, err := getEdgeNodeConfig(client, name, id)
152+
err := getEdgeNodeAndPublishData(client, d, name, id, resource)
131153
if err != nil {
132-
log.Printf("[ERROR] Failed to read config. Error: %s", err.Error())
133-
return diag.Errorf("[ERROR] edge node %s ( id: %s) not found. Err: %s",
134-
name, id, err.Error())
154+
return diag.Errorf("%s", err.Error())
135155
}
136-
// Take the Config and convert it to terraform object
137-
marshalData(d, flattenDeviceConfig(cfg, resource))
138156
return diags
139157
}
140158

provider/resource_edgenode.go

Lines changed: 102 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,6 @@ func edgeNodeUpdateBaseOs(client *zedcloudapi.Client, cfg *swagger_models.Device
9797
return nil
9898
}
9999

100-
// Create the Edge Node
101-
func createEdgeNodeResource(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
102-
return diag.Errorf("[ERROR] terraform-provider-zededa does not support Creation of Edge Nodes")
103-
}
104-
105100
func rdConfigItems(d *schema.ResourceData) ([]*swagger_models.EDConfigItem, error) {
106101
cfgItems := make([]*swagger_models.EDConfigItem, 0)
107102
val, exists := d.GetOk("config_items")
@@ -205,16 +200,38 @@ func setSystemInterface(cfg *swagger_models.DeviceConfig, d *schema.ResourceData
205200
return nil
206201
}
207202

208-
func rdDeviceConfig(cfg *swagger_models.DeviceConfig, d *schema.ResourceData) error {
203+
func setAdminState(cfg *swagger_models.DeviceConfig, d *schema.ResourceData, create bool) error {
204+
strVal := rdEntryStr(d, "adminstate_config")
205+
switch strVal {
206+
case "ADMIN_STATE_ACTIVE":
207+
case "ADMIN_STATE_INACTIVE":
208+
break
209+
default:
210+
return fmt.Errorf("adminstate_config must be specified and be one of " +
211+
"ADMIN_STATE_ACTIVE, ADMIN_STATE_INACTIVE")
212+
}
213+
if !create {
214+
// Device Update. If adminstate_config is ACTIVE and the device is
215+
// already in Registered state, do not change Admin State
216+
if strVal == "ADMIN_STATE_ACTIVE" && cfg.AdminState != nil &&
217+
*cfg.AdminState == swagger_models.AdminStateADMINSTATEREGISTERED {
218+
return nil
219+
}
220+
}
221+
adminstate := swagger_models.AdminState(strVal)
222+
cfg.AdminState = &adminstate
223+
return nil
224+
}
225+
226+
func rdDeviceConfig(cfg *swagger_models.DeviceConfig, d *schema.ResourceData, create bool) error {
227+
var err error
228+
cfg.Description = rdEntryStr(d, "description")
229+
cfg.Title = rdEntryStrPtrOrNil(d, "title")
230+
231+
setAdminState(cfg, d, create)
209232
cfg.AssetID = rdEntryStr(d, "asset_id")
210233
cfg.ClientIP = rdEntryStr(d, "client_ip")
211234
cfg.ClusterID = rdEntryStr(d, "cluster_id")
212-
cfg.Description = rdEntryStr(d, "description")
213-
eve_image_version := rdEntryStr(d, "eve_image_version")
214-
if eve_image_version == "" {
215-
return fmt.Errorf("eve_image_version must be specified.")
216-
}
217-
var err error
218235
cfg.ConfigItem, err = rdConfigItems(d)
219236
if err != nil {
220237
return err
@@ -223,18 +240,83 @@ func rdDeviceConfig(cfg *swagger_models.DeviceConfig, d *schema.ResourceData) er
223240
if err != nil {
224241
return err
225242
}
243+
eve_image_version := rdEntryStr(d, "eve_image_version")
244+
if eve_image_version == "" {
245+
return fmt.Errorf("eve_image_version must be specified.")
246+
}
247+
if create {
248+
// EVE image version is set here only during create. Eve Image change is
249+
// handled differently during update
250+
cfg.BaseImage = cfgBaseosForEveVersionStr(rdEntryStr(d, "eve_image_version"))
251+
}
226252
err = setSystemInterface(cfg, d)
227253
if err != nil {
228254
return err
229255
}
256+
if create {
257+
// model id and project id will be set only during create.
258+
cfg.ModelID = rdEntryStrPtrOrNil(d, "model_id")
259+
if cfg.ModelID == nil || *cfg.ModelID == "" {
260+
return fmt.Errorf("model_id must be specified for the EdgeNode.")
261+
}
262+
cfg.Obkey = rdEntryStr(d, "onboard_key")
263+
}
264+
projectIdPtr := rdEntryStrPtrOrNil(d, "project_id")
265+
if create {
266+
if projectIdPtr == nil || *projectIdPtr == "" {
267+
return fmt.Errorf("project_id must be specified for the EdgeNode.")
268+
}
269+
cfg.ProjectID = projectIdPtr
270+
} else {
271+
if cfg.ProjectID != nil && *cfg.ProjectID == *projectIdPtr {
272+
// Update. Project cannot be changed
273+
return fmt.Errorf("project_id cannot be changed after EdgeNode is "+
274+
"created. Current: %s, New: %s", *cfg.ProjectID, *projectIdPtr)
275+
}
276+
}
277+
cfg.Serialno = rdEntryStr(d, "serialno")
230278
cfg.Tags = rdEntryStrMap(d, "tags")
231-
cfg.Title = rdEntryStrPtrOrNil(d, "title")
232279
return nil
233280
}
234281

282+
// Create the Edge Node
283+
func createEdgeNodeResource(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
284+
var diags diag.Diagnostics
285+
286+
client := (meta.(Client)).Client
287+
name := rdEntryStr(d, "name")
288+
id := rdEntryStr(d, "id")
289+
errMsgPrefix := getErrMsgPrefix(name, id, "EdgeNode", "Create")
290+
if client == nil {
291+
return diag.Errorf("%s err: %s", errMsgPrefix, "nil Client")
292+
}
293+
cfg := &swagger_models.DeviceConfig{
294+
Name: &name,
295+
}
296+
err := rdDeviceConfig(cfg, d, true)
297+
if err != nil {
298+
return diag.Errorf("%s Error: %s", errMsgPrefix, err.Error())
299+
}
300+
log.Printf("[INFO] Creating EdgeNode: %s", name)
301+
client.XRequestIdPrefix = "TF-edgenode-create"
302+
rspData := &swagger_models.ZsrvResponse{}
303+
_, err = client.SendReq("POST", deviceUrlExtension, cfg, rspData)
304+
if err != nil {
305+
return diag.Errorf("%s err: %s", errMsgPrefix, err.Error())
306+
}
307+
id = rspData.ObjectID
308+
log.Printf("EdgeNode %s (ID: %s) Successfully created\n", rspData.ObjectName, id)
309+
d.SetId(id)
310+
err = getEdgeNodeAndPublishData(client, d, name, id, true)
311+
if err != nil {
312+
log.Printf("***[ERROR]- Failed to get EdgeNode: %s (ID: %s) after "+
313+
"creating it. Err: %s", name, id, err.Error())
314+
}
315+
return diags
316+
}
317+
235318
// Update the Resource Group
236319
func updateEdgeNodeResource(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
237-
// Warning or errors can be collected in a slice type
238320
var diags diag.Diagnostics
239321

240322
client := (meta.(Client)).Client
@@ -258,7 +340,7 @@ func updateEdgeNodeResource(ctx context.Context, d *schema.ResourceData, meta in
258340
"Object in zedcontrol is not same as expected by Terraform.",
259341
errMsgPrefix, id, cfg.ID)
260342
}
261-
err = rdDeviceConfig(cfg, d)
343+
err = rdDeviceConfig(cfg, d, false)
262344
if err != nil {
263345
return diag.Errorf("%s Error: %s", errMsgPrefix, err.Error())
264346
}
@@ -271,20 +353,22 @@ func updateEdgeNodeResource(ctx context.Context, d *schema.ResourceData, meta in
271353
if err != nil {
272354
return diag.Errorf("%s %s", errMsgPrefix, err.Error())
273355
}
356+
err = getEdgeNodeAndPublishData(client, d, name, id, true)
357+
if err != nil {
358+
return diag.Errorf("%s", err.Error())
359+
}
274360
log.Printf("[INFO] EdgeNode %s (ID: %s) Update Successful.", name, cfg.ID)
275361
return diags
276362
}
277363

278364
// Delete the Resource Group
279365
func deleteEdgeNodeResource(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
280-
// Warning or errors can be collected in a slice type
281366
var diags diag.Diagnostics
282367

283368
client := (meta.(Client)).Client
284369
name := rdEntryStr(d, "name")
285370
id := rdEntryStr(d, "id")
286-
errMsgPrefix := fmt.Sprintf("[ERROR] Edge Node %s ( id: %s) Delete Failed.",
287-
name, id)
371+
errMsgPrefix := getErrMsgPrefix(name, id, "Edge Node", "Delete")
288372
client.XRequestIdPrefix = "TF-edgenode-delete"
289373
urlExtension := getEdgeNodeUrl(name, id, "delete")
290374
rspData := &swagger_models.ZsrvResponse{}

provider/resource_edgenode_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ var rdEdgeNodeFullCfg = map[string]interface{}{
9191
*/
9292
},
9393
"memory": "",
94-
"model_id": "",
94+
"model_id": "Test Model",
9595
"project_id": "sample-project",
9696
"reset_counter": 10,
9797
"reset_time": "sample-time",
@@ -128,12 +128,12 @@ var efoEdgeNodeFullCfg = map[string]interface{}{
128128
"eve_image_version": rdEdgeNodeFullCfg["eve_image_version"],
129129
"interface": rdEdgeNodeFullCfg["interface"],
130130
"memory": 0,
131-
"model_id": "",
132-
"project_id": "",
131+
"model_id": rdEdgeNodeFullCfg["model_id"],
132+
"project_id": rdEdgeNodeFullCfg["project_id"],
133133
"reset_counter": 0,
134134
"reset_time": "",
135135
"revision": []interface{}{},
136-
"serialno": "",
136+
"serialno": rdEdgeNodeFullCfg["serialno"],
137137
"storage": 0,
138138
"tags": rdEdgeNodeFullCfg["tags"],
139139
"thread": 0,
@@ -174,7 +174,7 @@ func TestRDEdgeNodeConfig(t *testing.T) {
174174
cfg.Name = &name
175175
cfg.ID, ok = c.input["id"].(string)
176176
cfg.BaseImage = cfgBaseosForEveVersionStr(rdEntryStr(rd, "eve_image_version"))
177-
err := rdDeviceConfig(cfg, rd)
177+
err := rdDeviceConfig(cfg, rd, true)
178178
if err != nil {
179179
if !c.expectError {
180180
t.Fatalf("Test Failed: %s\n"+

schemas/edgenode.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,17 @@ var EdgeNodeSchema = map[string]*schema.Schema{
116116

117117
// Rest of the fields must be in the alphabetical order of keys
118118
"adminstate": {
119-
Type: schema.TypeString,
120-
Computed: true,
121-
Description: "Administrative state of device",
119+
Type: schema.TypeString,
120+
Computed: true,
121+
Description: "Current Administrative state of device. Apart from " +
122+
"states specified in adminstate_config field, this can also be in " +
123+
"ADMIN_STATE_REGISTERED (Device is Active and registered)",
124+
},
125+
"adminstate_config": {
126+
Type: schema.TypeString,
127+
Optional: true,
128+
Description: "Administrative state of device. Required field. " +
129+
"Valid Values to configure: ADMIN_STATE_ACTIVE, ADMIN_STATE_INACTIVE",
122130
},
123131
"asset_id": {
124132
Type: schema.TypeString,
@@ -172,15 +180,17 @@ var EdgeNodeSchema = map[string]*schema.Schema{
172180
},
173181
"model_id": {
174182
Type: schema.TypeString,
175-
Computed: true,
183+
Optional: true,
176184
Description: "ID of device model object for the Edge Node",
177185
},
178-
// Project ID for the device cannot be changed. Since the device is created
179-
// in ZEDCloud, set Computed=true for project_id. This is different from
180-
// other objects.
186+
"onboard_key": &schema.Schema{
187+
Type: schema.TypeString,
188+
Optional: true,
189+
Description: "Onboard Key for the device",
190+
},
181191
"project_id": &schema.Schema{
182192
Type: schema.TypeString,
183-
Computed: true,
193+
Optional: true,
184194
Description: "ID of the project to which the Object belongs",
185195
},
186196
"reset_counter": {
@@ -196,7 +206,7 @@ var EdgeNodeSchema = map[string]*schema.Schema{
196206
"revision": revisionSchema,
197207
"serialno": {
198208
Type: schema.TypeString,
199-
Computed: true,
209+
Optional: true,
200210
Description: "Edge Node serial number",
201211
},
202212
"storage": {
@@ -208,7 +218,7 @@ var EdgeNodeSchema = map[string]*schema.Schema{
208218
"thread": {
209219
Type: schema.TypeInt,
210220
Computed: true,
211-
Description: "Threads",
221+
Description: "Number of Threads",
212222
},
213223
"utype": {
214224
Type: schema.TypeString,

0 commit comments

Comments
 (0)