Skip to content

Commit 5ccfd16

Browse files
committed
Implement ODS runbook invocation resource
Signed-off-by: Kobi Samoray <[email protected]>
1 parent 9d7b9e7 commit 5ccfd16

4 files changed

+408
-0
lines changed

Diff for: nsxt/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ func Provider() *schema.Provider {
442442
"nsxt_policy_lb_http_application_profile": resourceNsxtPolicyLBHttpApplicationProfile(),
443443
"nsxt_policy_security_policy_rule": resourceNsxtPolicySecurityPolicyRule(),
444444
"nsxt_policy_parent_security_policy": resourceNsxtPolicyParentSecurityPolicy(),
445+
"nsxt_policy_ods_runbook_invocation": resourceNsxtPolicyODSRunbookInvocation(),
445446
},
446447

447448
ConfigureFunc: providerConfigure,

Diff for: nsxt/resource_nsxt_policy_ods_runbook_invocation.go

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/* Copyright © 2023 VMware, Inc. All Rights Reserved.
2+
SPDX-License-Identifier: MPL-2.0 */
3+
4+
package nsxt
5+
6+
import (
7+
"fmt"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
"github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client"
11+
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/sha"
12+
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model"
13+
)
14+
15+
func resourceNsxtPolicyODSRunbookInvocation() *schema.Resource {
16+
return &schema.Resource{
17+
Create: resourceNsxtPolicyODSRunbookInvocationCreate,
18+
Read: resourceNsxtPolicyODSRunbookInvocationRead,
19+
Update: resourceNsxtPolicyODSRunbookInvocationUpdate,
20+
Delete: resourceNsxtPolicyODSRunbookInvocationDelete,
21+
Importer: &schema.ResourceImporter{
22+
State: nsxtPolicyPathResourceImporter,
23+
},
24+
25+
Schema: map[string]*schema.Schema{
26+
"nsx_id": getNsxIDSchema(),
27+
"path": getPathSchema(),
28+
// Due to a bug, invocations with a display_name specification fail, and when there's none set, NSX assigns
29+
// the id value to the display name attribute. This should work around that bug.
30+
"display_name": {
31+
Type: schema.TypeString,
32+
Description: "Display name for this resource",
33+
Optional: true,
34+
Computed: true,
35+
},
36+
"revision": getRevisionSchema(),
37+
"argument": {
38+
Type: schema.TypeSet,
39+
Optional: true,
40+
Description: "Arguments for runbook invocation",
41+
Elem: &schema.Resource{
42+
Schema: map[string]*schema.Schema{
43+
"key": {
44+
Type: schema.TypeString,
45+
Required: true,
46+
Description: "Key",
47+
},
48+
"value": {
49+
Type: schema.TypeString,
50+
Required: true,
51+
Description: "Value",
52+
},
53+
},
54+
},
55+
},
56+
"runbook_path": getPolicyPathSchema(true, true, "Path of runbook object"),
57+
"target_node": {
58+
Type: schema.TypeString,
59+
Optional: true,
60+
Description: "Identifier of an appliance node or transport node",
61+
ForceNew: true,
62+
},
63+
},
64+
}
65+
}
66+
67+
func getODSRunbookInvocationFromSchema(id string, d *schema.ResourceData) model.OdsRunbookInvocation {
68+
displayName := d.Get("display_name").(string)
69+
runbookPath := d.Get("runbook_path").(string)
70+
targetNode := d.Get("target_node").(string)
71+
72+
var arguments []model.UnboundedKeyValuePair
73+
for _, arg := range d.Get("argument").(*schema.Set).List() {
74+
argMap := arg.(map[string]interface{})
75+
key := argMap["key"].(string)
76+
value := argMap["value"].(string)
77+
item := model.UnboundedKeyValuePair{
78+
Key: &key,
79+
Value: &value,
80+
}
81+
arguments = append(arguments, item)
82+
}
83+
84+
obj := model.OdsRunbookInvocation{
85+
Id: &id,
86+
RunbookPath: &runbookPath,
87+
Arguments: arguments,
88+
TargetNode: &targetNode,
89+
}
90+
if displayName != "" {
91+
obj.DisplayName = &displayName
92+
}
93+
94+
return obj
95+
}
96+
97+
func resourceNsxtPolicyODSRunbookInvocationCreate(d *schema.ResourceData, m interface{}) error {
98+
// Initialize resource Id and verify this ID is not yet used
99+
id, err := getOrGenerateID(d, m, resourceNsxtPolicyODSRunbookInvocationExists)
100+
if err != nil {
101+
return err
102+
}
103+
104+
connector := getPolicyConnector(m)
105+
client := sha.NewRunbookInvocationsClient(connector)
106+
107+
obj := getODSRunbookInvocationFromSchema(id, d)
108+
err = client.Create(id, obj)
109+
if err != nil {
110+
return handleCreateError("OdsRunbookInvocation", id, err)
111+
}
112+
113+
d.SetId(id)
114+
d.Set("nsx_id", id)
115+
return resourceNsxtPolicyODSRunbookInvocationRead(d, m)
116+
}
117+
118+
func resourceNsxtPolicyODSRunbookInvocationExists(id string, connector client.Connector, isGlobalManager bool) (bool, error) {
119+
var err error
120+
client := sha.NewRunbookInvocationsClient(connector)
121+
_, err = client.Get(id)
122+
123+
if err == nil {
124+
return true, nil
125+
}
126+
127+
if isNotFoundError(err) {
128+
return false, nil
129+
}
130+
131+
return false, logAPIError("Error retrieving resource", err)
132+
}
133+
134+
func resourceNsxtPolicyODSRunbookInvocationRead(d *schema.ResourceData, m interface{}) error {
135+
connector := getPolicyConnector(m)
136+
137+
id := d.Id()
138+
if id == "" {
139+
return fmt.Errorf("error obtaining OdsRunbookInvocation ID")
140+
}
141+
142+
client := sha.NewRunbookInvocationsClient(connector)
143+
var err error
144+
obj, err := client.Get(id)
145+
if err != nil {
146+
return handleReadError(d, "OdsRunbookInvocation", id, err)
147+
}
148+
149+
if obj.DisplayName != nil && *obj.DisplayName != "" {
150+
d.Set("display_name", obj.DisplayName)
151+
}
152+
d.Set("nsx_id", id)
153+
d.Set("path", obj.Path)
154+
d.Set("revision", obj.Revision)
155+
156+
d.Set("runbook_path", obj.RunbookPath)
157+
d.Set("target_node", obj.TargetNode)
158+
159+
var argList []map[string]interface{}
160+
for _, arg := range obj.Arguments {
161+
argData := make(map[string]interface{})
162+
argData["key"] = arg.Key
163+
argData["value"] = arg.Value
164+
argList = append(argList, argData)
165+
}
166+
d.Set("argument", argList)
167+
168+
return nil
169+
}
170+
171+
func resourceNsxtPolicyODSRunbookInvocationUpdate(d *schema.ResourceData, m interface{}) error {
172+
return resourceNsxtPolicyODSRunbookInvocationRead(d, m)
173+
}
174+
175+
func resourceNsxtPolicyODSRunbookInvocationDelete(d *schema.ResourceData, m interface{}) error {
176+
id := d.Id()
177+
if id == "" {
178+
return fmt.Errorf("error obtaining OdsRunbookInvocation ID")
179+
}
180+
181+
connector := getPolicyConnector(m)
182+
var err error
183+
client := sha.NewRunbookInvocationsClient(connector)
184+
err = client.Delete(id)
185+
186+
if err != nil {
187+
return handleDeleteError("OdsRunbookInvocation", id, err)
188+
}
189+
190+
return nil
191+
}
+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/* Copyright © 2023 VMware, Inc. All Rights Reserved.
2+
SPDX-License-Identifier: MPL-2.0 */
3+
4+
package nsxt
5+
6+
import (
7+
"fmt"
8+
"testing"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
12+
)
13+
14+
func TestAccResourceNsxtPolicyODSRunbookInvocation_basic(t *testing.T) {
15+
name := getAccTestResourceName()
16+
testResourceName := "nsxt_policy_ods_runbook_invocation.test"
17+
resource.Test(t, resource.TestCase{
18+
PreCheck: func() {
19+
testAccOnlyLocalManager(t)
20+
testAccPreCheck(t)
21+
testAccNSXVersion(t, "4.1.1")
22+
testAccEnvDefined(t, "NSXT_TEST_HOST_TRANSPORT_NODE")
23+
},
24+
Providers: testAccProviders,
25+
CheckDestroy: func(state *terraform.State) error {
26+
return testAccNsxtPolicyODSRunbookInvocationCheckDestroy(state, name)
27+
},
28+
Steps: []resource.TestStep{
29+
{
30+
Config: testAccNsxtPolicyODSRunbookInvocationCreateTemplate(name, "OverlayTunnel", `
31+
argument {
32+
key = "src"
33+
value = "192.168.0.11"
34+
}
35+
argument {
36+
key = "dst"
37+
value = "192.168.0.10"
38+
}
39+
`),
40+
Check: resource.ComposeTestCheckFunc(
41+
testAccNsxtPolicyODSRunbookInvocationExists(name, testResourceName),
42+
resource.TestCheckResourceAttrSet(testResourceName, "target_node"),
43+
resource.TestCheckResourceAttrSet(testResourceName, "runbook_path"),
44+
),
45+
},
46+
},
47+
})
48+
}
49+
50+
func TestAccResourceNsxtPolicyODSRunbookInvocation_import(t *testing.T) {
51+
52+
name := getAccTestResourceName()
53+
testResourceName := "nsxt_policy_ods_runbook_invocation.test"
54+
resource.Test(t, resource.TestCase{
55+
PreCheck: func() {
56+
testAccPreCheck(t)
57+
testAccNSXVersion(t, "4.1.1")
58+
testAccOnlyLocalManager(t)
59+
},
60+
Providers: testAccProviders,
61+
CheckDestroy: func(state *terraform.State) error {
62+
return testAccNsxtPolicyODSRunbookInvocationCheckDestroy(state, name)
63+
},
64+
Steps: []resource.TestStep{
65+
{
66+
Config: testAccNsxtPolicyODSRunbookInvocationCreateTemplate(name, "OverlayTunnel", `
67+
argument {
68+
key = "src"
69+
value = "192.168.0.11"
70+
}
71+
argument {
72+
key = "dst"
73+
value = "192.168.0.10"
74+
}
75+
`),
76+
},
77+
{
78+
ResourceName: testResourceName,
79+
ImportState: true,
80+
ImportStateVerify: true,
81+
ImportStateIdFunc: testAccResourceNsxtPolicyImportIDRetriever(testResourceName),
82+
},
83+
},
84+
})
85+
}
86+
87+
func testAccNsxtPolicyODSRunbookInvocationCheckDestroy(state *terraform.State, displayName string) error {
88+
connector := getPolicyConnector(testAccProvider.Meta().(nsxtClients))
89+
for _, rs := range state.RootModule().Resources {
90+
91+
if rs.Type != "nsxt_policy_ods_runbook_invocation" {
92+
continue
93+
}
94+
95+
resourceID := rs.Primary.Attributes["id"]
96+
exists, err := resourceNsxtPolicyODSRunbookInvocationExists(resourceID, connector, testAccIsGlobalManager())
97+
if err == nil {
98+
return err
99+
}
100+
101+
if exists {
102+
return fmt.Errorf("policy ODSRunbookInvocation %s still exists", displayName)
103+
}
104+
}
105+
return nil
106+
}
107+
108+
func testAccNsxtPolicyODSRunbookInvocationExists(displayName string, resourceName string) resource.TestCheckFunc {
109+
return func(state *terraform.State) error {
110+
111+
connector := getPolicyConnector(testAccProvider.Meta().(nsxtClients))
112+
113+
rs, ok := state.RootModule().Resources[resourceName]
114+
if !ok {
115+
return fmt.Errorf("policy ODSRunbookInvocation resource %s not found in resources", resourceName)
116+
}
117+
118+
resourceID := rs.Primary.ID
119+
if resourceID == "" {
120+
return fmt.Errorf("policy ODSRunbookInvocation resource ID not set in resources")
121+
}
122+
123+
exists, err := resourceNsxtPolicyODSRunbookInvocationExists(resourceID, connector, testAccIsGlobalManager())
124+
if err != nil {
125+
return err
126+
}
127+
if !exists {
128+
return fmt.Errorf("policy ODSRunbookInvocation %s does not exist", resourceID)
129+
}
130+
131+
return nil
132+
}
133+
}
134+
135+
func testAccNsxtPolicyODSRunbookInvocationCreateTemplate(name, runbook, arguments string) string {
136+
htnName := getHostTransportNodeName()
137+
return testAccNsxtPolicyODSPredefinedRunbookReadTemplate(runbook) + fmt.Sprintf(`
138+
data "nsxt_policy_host_transport_node" "test" {
139+
display_name = "%s"
140+
}
141+
142+
resource "nsxt_policy_ods_runbook_invocation" "test" {
143+
// Use nsx_id here to address a backend issue.
144+
nsx_id = "%s"
145+
runbook_path = data.nsxt_policy_ods_pre_defined_runbook.test.path
146+
%s
147+
target_node = data.nsxt_policy_host_transport_node.test.unique_id
148+
}
149+
`, htnName, name, arguments)
150+
}

0 commit comments

Comments
 (0)