diff --git a/nsxt/data_source_nsxt_policy_ods_pre_defined_runbook.go b/nsxt/data_source_nsxt_policy_ods_pre_defined_runbook.go new file mode 100644 index 000000000..eeeb82a28 --- /dev/null +++ b/nsxt/data_source_nsxt_policy_ods_pre_defined_runbook.go @@ -0,0 +1,82 @@ +/* Copyright © 2023 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/sha" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" +) + +func dataSourceNsxtPolicyODSPreDefinedRunbook() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNsxtPolicyODSPreDefinedRunbookRead, + + Schema: map[string]*schema.Schema{ + "id": getDataSourceIDSchema(), + "display_name": getDataSourceDisplayNameSchema(), + "description": getDataSourceDescriptionSchema(), + "path": getPathSchema(), + }, + } +} + +func dataSourceNsxtPolicyODSPreDefinedRunbookRead(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + client := sha.NewPreDefinedRunbooksClient(connector) + + objID := d.Get("id").(string) + objName := d.Get("display_name").(string) + var obj model.OdsPredefinedRunbook + if objID != "" { + // Get by id + objGet, err := client.Get(objID) + if err != nil { + return handleDataSourceReadError(d, "OdsPredefinedRunbook", objID, err) + } + obj = objGet + } else if objName == "" { + return fmt.Errorf("error obtaining OdsPredefinedRunbook ID or name during read") + } else { + // Get by full name/prefix + objList, err := client.List(nil, nil, nil, nil, nil, nil) + if err != nil { + return handleListError("OdsPredefinedRunbook", err) + } + // go over the list to find the correct one (prefer a perfect match. If not - prefix match) + var perfectMatch []model.OdsPredefinedRunbook + var prefixMatch []model.OdsPredefinedRunbook + for _, objInList := range objList.Results { + if strings.HasPrefix(*objInList.DisplayName, objName) { + prefixMatch = append(prefixMatch, objInList) + } + if *objInList.DisplayName == objName { + perfectMatch = append(perfectMatch, objInList) + } + } + if len(perfectMatch) > 0 { + if len(perfectMatch) > 1 { + return fmt.Errorf("found multiple OdsPredefinedRunbook with name '%s'", objName) + } + obj = perfectMatch[0] + } else if len(prefixMatch) > 0 { + if len(prefixMatch) > 1 { + return fmt.Errorf("found multiple OdsPredefinedRunbooks with name starting with '%s'", objName) + } + obj = prefixMatch[0] + } else { + return fmt.Errorf("OdsPredefinedRunbook with name '%s' was not found", objName) + } + } + + d.SetId(*obj.Id) + d.Set("display_name", obj.DisplayName) + d.Set("description", obj.Description) + d.Set("path", obj.Path) + + return nil +} diff --git a/nsxt/data_source_nsxt_policy_ods_pre_defined_runbook_test.go b/nsxt/data_source_nsxt_policy_ods_pre_defined_runbook_test.go new file mode 100644 index 000000000..8d360a01a --- /dev/null +++ b/nsxt/data_source_nsxt_policy_ods_pre_defined_runbook_test.go @@ -0,0 +1,41 @@ +/* Copyright © 2023 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceNsxtPolicyODSPredefinedRunbook_basic(t *testing.T) { + name := "ControllerConn" + testResourceName := "data.nsxt_policy_ods_pre_defined_runbook.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccOnlyLocalManager(t) + testAccPreCheck(t) + testAccNSXVersion(t, "4.1.0") + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccNsxtPolicyODSPredefinedRunbookReadTemplate(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(testResourceName, "display_name", name), + resource.TestCheckResourceAttrSet(testResourceName, "path"), + ), + }, + }, + }) +} + +func testAccNsxtPolicyODSPredefinedRunbookReadTemplate(name string) string { + return fmt.Sprintf(` +data "nsxt_policy_ods_pre_defined_runbook" "test" { + display_name = "%s" +}`, name) +} diff --git a/nsxt/provider.go b/nsxt/provider.go index b97318655..cbb692613 100644 --- a/nsxt/provider.go +++ b/nsxt/provider.go @@ -324,6 +324,7 @@ func Provider() *schema.Provider { "nsxt_policy_vtep_ha_host_switch_profile": dataSourceNsxtVtepHAHostSwitchProfile(), "nsxt_policy_distributed_flood_protection_profile": dataSourceNsxtPolicyDistributedFloodProtectionProfile(), "nsxt_policy_gateway_flood_protection_profile": dataSourceNsxtPolicyGatewayFloodProtectionProfile(), + "nsxt_policy_ods_pre_defined_runbook": dataSourceNsxtPolicyODSPreDefinedRunbook(), }, ResourcesMap: map[string]*schema.Resource{ @@ -492,6 +493,7 @@ func Provider() *schema.Provider { "nsxt_policy_gateway_flood_protection_profile_binding": resourceNsxtPolicyGatewayFloodProtectionProfileBinding(), "nsxt_policy_compute_sub_cluster": resourceNsxtPolicyComputeSubCluster(), "nsxt_policy_tier0_inter_vrf_routing": resourceNsxtPolicyTier0InterVRFRouting(), + "nsxt_policy_ods_runbook_invocation": resourceNsxtPolicyODSRunbookInvocation(), }, ConfigureFunc: providerConfigure, diff --git a/nsxt/resource_nsxt_policy_ods_runbook_invocation.go b/nsxt/resource_nsxt_policy_ods_runbook_invocation.go new file mode 100644 index 000000000..4000c221e --- /dev/null +++ b/nsxt/resource_nsxt_policy_ods_runbook_invocation.go @@ -0,0 +1,191 @@ +/* Copyright © 2023 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/sha" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" +) + +func resourceNsxtPolicyODSRunbookInvocation() *schema.Resource { + return &schema.Resource{ + Create: resourceNsxtPolicyODSRunbookInvocationCreate, + Read: resourceNsxtPolicyODSRunbookInvocationRead, + Update: resourceNsxtPolicyODSRunbookInvocationUpdate, + Delete: resourceNsxtPolicyODSRunbookInvocationDelete, + Importer: &schema.ResourceImporter{ + State: nsxtPolicyPathResourceImporter, + }, + + Schema: map[string]*schema.Schema{ + "nsx_id": getNsxIDSchema(), + "path": getPathSchema(), + // Due to a bug, invocations with a display_name specification fail, and when there's none set, NSX assigns + // the id value to the display name attribute. This should work around that bug. + "display_name": { + Type: schema.TypeString, + Description: "Display name for this resource", + Optional: true, + Computed: true, + }, + "revision": getRevisionSchema(), + "argument": { + Type: schema.TypeSet, + Optional: true, + Description: "Arguments for runbook invocation", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + Description: "Key", + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: "Value", + }, + }, + }, + }, + "runbook_path": getPolicyPathSchema(true, true, "Path of runbook object"), + "target_node": { + Type: schema.TypeString, + Optional: true, + Description: "Identifier of an appliance node or transport node", + ForceNew: true, + }, + }, + } +} + +func getODSRunbookInvocationFromSchema(id string, d *schema.ResourceData) model.OdsRunbookInvocation { + displayName := d.Get("display_name").(string) + runbookPath := d.Get("runbook_path").(string) + targetNode := d.Get("target_node").(string) + + var arguments []model.UnboundedKeyValuePair + for _, arg := range d.Get("argument").(*schema.Set).List() { + argMap := arg.(map[string]interface{}) + key := argMap["key"].(string) + value := argMap["value"].(string) + item := model.UnboundedKeyValuePair{ + Key: &key, + Value: &value, + } + arguments = append(arguments, item) + } + + obj := model.OdsRunbookInvocation{ + Id: &id, + RunbookPath: &runbookPath, + Arguments: arguments, + TargetNode: &targetNode, + } + if displayName != "" { + obj.DisplayName = &displayName + } + + return obj +} + +func resourceNsxtPolicyODSRunbookInvocationCreate(d *schema.ResourceData, m interface{}) error { + // Initialize resource Id and verify this ID is not yet used + id, err := getOrGenerateID(d, m, resourceNsxtPolicyODSRunbookInvocationExists) + if err != nil { + return err + } + + connector := getPolicyConnector(m) + client := sha.NewRunbookInvocationsClient(connector) + + obj := getODSRunbookInvocationFromSchema(id, d) + err = client.Create(id, obj) + if err != nil { + return handleCreateError("OdsRunbookInvocation", id, err) + } + + d.SetId(id) + d.Set("nsx_id", id) + return resourceNsxtPolicyODSRunbookInvocationRead(d, m) +} + +func resourceNsxtPolicyODSRunbookInvocationExists(id string, connector client.Connector, isGlobalManager bool) (bool, error) { + var err error + client := sha.NewRunbookInvocationsClient(connector) + _, err = client.Get(id) + + if err == nil { + return true, nil + } + + if isNotFoundError(err) { + return false, nil + } + + return false, logAPIError("Error retrieving resource", err) +} + +func resourceNsxtPolicyODSRunbookInvocationRead(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + + id := d.Id() + if id == "" { + return fmt.Errorf("error obtaining OdsRunbookInvocation ID") + } + + client := sha.NewRunbookInvocationsClient(connector) + var err error + obj, err := client.Get(id) + if err != nil { + return handleReadError(d, "OdsRunbookInvocation", id, err) + } + + if obj.DisplayName != nil && *obj.DisplayName != "" { + d.Set("display_name", obj.DisplayName) + } + d.Set("nsx_id", id) + d.Set("path", obj.Path) + d.Set("revision", obj.Revision) + + d.Set("runbook_path", obj.RunbookPath) + d.Set("target_node", obj.TargetNode) + + var argList []map[string]interface{} + for _, arg := range obj.Arguments { + argData := make(map[string]interface{}) + argData["key"] = arg.Key + argData["value"] = arg.Value + argList = append(argList, argData) + } + d.Set("argument", argList) + + return nil +} + +func resourceNsxtPolicyODSRunbookInvocationUpdate(d *schema.ResourceData, m interface{}) error { + return resourceNsxtPolicyODSRunbookInvocationRead(d, m) +} + +func resourceNsxtPolicyODSRunbookInvocationDelete(d *schema.ResourceData, m interface{}) error { + id := d.Id() + if id == "" { + return fmt.Errorf("error obtaining OdsRunbookInvocation ID") + } + + connector := getPolicyConnector(m) + var err error + client := sha.NewRunbookInvocationsClient(connector) + err = client.Delete(id) + + if err != nil { + return handleDeleteError("OdsRunbookInvocation", id, err) + } + + return nil +} diff --git a/nsxt/resource_nsxt_policy_ods_runbook_invocation_test.go b/nsxt/resource_nsxt_policy_ods_runbook_invocation_test.go new file mode 100644 index 000000000..77011427d --- /dev/null +++ b/nsxt/resource_nsxt_policy_ods_runbook_invocation_test.go @@ -0,0 +1,150 @@ +/* Copyright © 2023 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccResourceNsxtPolicyODSRunbookInvocation_basic(t *testing.T) { + name := getAccTestResourceName() + testResourceName := "nsxt_policy_ods_runbook_invocation.test" + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccOnlyLocalManager(t) + testAccPreCheck(t) + testAccNSXVersion(t, "4.2.0") + testAccEnvDefined(t, "NSXT_TEST_HOST_TRANSPORT_NODE") + }, + Providers: testAccProviders, + CheckDestroy: func(state *terraform.State) error { + return testAccNsxtPolicyODSRunbookInvocationCheckDestroy(state, name) + }, + Steps: []resource.TestStep{ + { + Config: testAccNsxtPolicyODSRunbookInvocationCreateTemplate(name, "OverlayTunnel", ` + argument { + key = "src" + value = "192.168.0.11" + } + argument { + key = "dst" + value = "192.168.0.10" + } +`), + Check: resource.ComposeTestCheckFunc( + testAccNsxtPolicyODSRunbookInvocationExists(name, testResourceName), + resource.TestCheckResourceAttrSet(testResourceName, "target_node"), + resource.TestCheckResourceAttrSet(testResourceName, "runbook_path"), + ), + }, + }, + }) +} + +func TestAccResourceNsxtPolicyODSRunbookInvocation_import(t *testing.T) { + + name := getAccTestResourceName() + testResourceName := "nsxt_policy_ods_runbook_invocation.test" + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccNSXVersion(t, "4.2.0") + testAccOnlyLocalManager(t) + testAccEnvDefined(t, "NSXT_TEST_HOST_TRANSPORT_NODE") + }, + Providers: testAccProviders, + CheckDestroy: func(state *terraform.State) error { + return testAccNsxtPolicyODSRunbookInvocationCheckDestroy(state, name) + }, + Steps: []resource.TestStep{ + { + Config: testAccNsxtPolicyODSRunbookInvocationCreateTemplate(name, "OverlayTunnel", ` + argument { + key = "src" + value = "192.168.0.11" + } + argument { + key = "dst" + value = "192.168.0.10" + } +`), + }, + { + ResourceName: testResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccResourceNsxtPolicyImportIDRetriever(testResourceName), + }, + }, + }) +} + +func testAccNsxtPolicyODSRunbookInvocationCheckDestroy(state *terraform.State, displayName string) error { + connector := getPolicyConnector(testAccProvider.Meta().(nsxtClients)) + for _, rs := range state.RootModule().Resources { + + if rs.Type != "nsxt_policy_ods_runbook_invocation" { + continue + } + + resourceID := rs.Primary.Attributes["id"] + exists, err := resourceNsxtPolicyODSRunbookInvocationExists(resourceID, connector, testAccIsGlobalManager()) + if err == nil { + return err + } + + if exists { + return fmt.Errorf("policy ODSRunbookInvocation %s still exists", displayName) + } + } + return nil +} + +func testAccNsxtPolicyODSRunbookInvocationExists(displayName string, resourceName string) resource.TestCheckFunc { + return func(state *terraform.State) error { + + connector := getPolicyConnector(testAccProvider.Meta().(nsxtClients)) + + rs, ok := state.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("policy ODSRunbookInvocation resource %s not found in resources", resourceName) + } + + resourceID := rs.Primary.ID + if resourceID == "" { + return fmt.Errorf("policy ODSRunbookInvocation resource ID not set in resources") + } + + exists, err := resourceNsxtPolicyODSRunbookInvocationExists(resourceID, connector, testAccIsGlobalManager()) + if err != nil { + return err + } + if !exists { + return fmt.Errorf("policy ODSRunbookInvocation %s does not exist", resourceID) + } + + return nil + } +} + +func testAccNsxtPolicyODSRunbookInvocationCreateTemplate(name, runbook, arguments string) string { + htnName := getHostTransportNodeName() + return testAccNsxtPolicyODSPredefinedRunbookReadTemplate(runbook) + fmt.Sprintf(` +data "nsxt_policy_host_transport_node" "test" { + display_name = "%s" +} + +resource "nsxt_policy_ods_runbook_invocation" "test" { + display_name = "%s" + runbook_path = data.nsxt_policy_ods_pre_defined_runbook.test.path +%s + target_node = data.nsxt_policy_host_transport_node.test.unique_id +} +`, htnName, name, arguments) +} diff --git a/website/docs/d/policy_ods_pre_defined_runbook.html.markdown b/website/docs/d/policy_ods_pre_defined_runbook.html.markdown new file mode 100644 index 000000000..b526a29d9 --- /dev/null +++ b/website/docs/d/policy_ods_pre_defined_runbook.html.markdown @@ -0,0 +1,31 @@ +--- +subcategory: "ODS Runbook" +layout: "nsxt" +page_title: "NSXT: policy_ods_pre_defined_runbook" +description: Policy ODS pre-defined runbook data source. +--- + +# nsxt_policy_ods_pre_defined_runbook + +This data source provides information about policy ODS pre-defined runbook configured on NSX. +This data source is applicable to NSX Policy Manager. + +## Example Usage + +```hcl +data "nsxt_policy_ods_pre_defined_runbook" "test" { + display_name = "OverlayTunnel" +} +``` + +## Argument Reference + +* `id` - (Optional) The ID of ODS pre-defined runbook to retrieve. If ID is specified, no additional argument should be configured. +* `display_name` - (Optional) The Display Name prefix of the ODS pre-defined runbook to retrieve. + +## Attributes Reference + +In addition to arguments listed above, the following attributes are exported: + +* `description` - The description of the resource. +* `path` - The NSX path of the policy resource. diff --git a/website/docs/r/policy_ods_runbook_invocation.html.markdown b/website/docs/r/policy_ods_runbook_invocation.html.markdown new file mode 100644 index 000000000..fd2a599de --- /dev/null +++ b/website/docs/r/policy_ods_runbook_invocation.html.markdown @@ -0,0 +1,66 @@ +--- +subcategory: "Beta" +layout: "nsxt" +page_title: "NSXT: nsxt_policy_ods_runbook_invocation" +description: A resource to configure ODS runbook invocation in NSX Policy manager. +--- + +# nsxt_policy_ods_runbook_invocation + +This resource provides a method for the management of a ODS runbook invocation. + +## Example Usage + +```hcl +data "nsxt_policy_host_transport_node" "tn1" { + display_name = "some_node_name.org" +} + +data "nsxt_policy_ods_pre_defined_runbook" "ot_runbook" { + display_name = "OverlayTunnel" +} + +resource "nsxt_policy_ods_runbook_invocation" "test" { + display_name = "overlay_tunnel_invocation" + runbook_path = data.nsxt_policy_ods_pre_defined_runbook.ot_runbook.path + argument { + key = "src" + value = "192.168.0.11" + } + argument { + key = "dst" + value = "192.168.0.10" + } + target_node = data.nsxt_policy_host_transport_node.tn1.id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `display_name` - (Required) Display name of the resource. +* `nsx_id` - (Optional) The NSX ID of this resource. If set, this ID will be used to create the policy resource. +* `argument` - (Optional) Arguments for runbook invocation. + * `key` - (Required) Key + * `value` - (Required) Value +* `runbook_path` - (Required) Path of runbook object. +* `target_node` - (Optional) Identifier of an appliance node or transport node. + +## Attributes Reference + +In addition to arguments listed above, the following attributes are exported: + +* `revision` - Indicates current revision number of the object as seen by NSX-T API server. This attribute can be useful for debugging. +* `path` - The NSX path of the policy resource. + +## Importing + +An existing policy ODS runbook invocation can be [imported][docs-import] into this resource, via the following command: + +[docs-import]: https://www.terraform.io/cli/import + +``` +terraform import nsxt_policy_ods_runbook_invocation.test POLICY_PATH +``` +The above command imports the policy ODS runbook invocation named `test` for policy path `POLICY_PATH`.