diff --git a/examples/categories_v2/main.tf b/examples/categories_v2/main.tf new file mode 100644 index 000000000..03c498beb --- /dev/null +++ b/examples/categories_v2/main.tf @@ -0,0 +1,36 @@ +terraform{ + required_providers { + nutanix = { + source = "nutanix/nutanix" + version = "1.3.0" + } + } +} + +#definig nutanix configuration +provider "nutanix"{ + username = var.nutanix_username + password = var.nutanix_password + endpoint = var.nutanix_endpoint + port = 9440 + insecure = true +} + + + +#creating category +resource "nutanix_category_v2" "example" { + key = "category_example_key" + value = "category_example_value" + description = "category example description" +} + + +#pull all categories data +data "nutanix_categories_v2" "clusters"{} + + +# get category by ext id +data "nutanix_category_v2" "example" { + ext_id = resource.nutanix_category_v2.example.ext_id +} \ No newline at end of file diff --git a/examples/categories_v2/terraform.tfvars b/examples/categories_v2/terraform.tfvars new file mode 100644 index 000000000..0965271af --- /dev/null +++ b/examples/categories_v2/terraform.tfvars @@ -0,0 +1,4 @@ +#define values to the variables to be used in terraform file +nutanix_username = "admin" +nutanix_password = "password" +nutanix_endpoint = "10.xx.xx.xx" diff --git a/examples/categories_v2/variables.tf b/examples/categories_v2/variables.tf new file mode 100644 index 000000000..4cb0b2cbd --- /dev/null +++ b/examples/categories_v2/variables.tf @@ -0,0 +1,10 @@ +#define the type of variables to be used in terraform file +variable "nutanix_username" { + type = string +} +variable "nutanix_password" { + type = string +} +variable "nutanix_endpoint" { + type = string +} \ No newline at end of file diff --git a/go.mod b/go.mod index 9185d6583..c5fd8d494 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16 v16.8.0-5280 github.com/nutanix-core/ntnx-api-golang-sdk-internal/clustermgmt-go-client/v16 v16.9.0-8538 github.com/nutanix-core/ntnx-api-golang-sdk-internal/prism-go-client/v16 v16.9.0-8500 + github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4 v4.0.1-beta.1 github.com/spf13/cast v1.3.1 github.com/stretchr/testify v1.7.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 26a09d853..463f95ea0 100644 --- a/go.sum +++ b/go.sum @@ -458,6 +458,8 @@ github.com/nutanix-core/ntnx-api-golang-sdk-internal/clustermgmt-go-client/v16 v github.com/nutanix-core/ntnx-api-golang-sdk-internal/clustermgmt-go-client/v16 v16.9.0-8538/go.mod h1:Wt2vo6h0QCGvQGKyY2Tw9OOU0dhhtjRL5nTd0Lx8Gho= github.com/nutanix-core/ntnx-api-golang-sdk-internal/prism-go-client/v16 v16.9.0-8500 h1:UPGaPcMuM30BTQ6FflAgF5LP/8t8/zVDFIOeZAtXn+8= github.com/nutanix-core/ntnx-api-golang-sdk-internal/prism-go-client/v16 v16.9.0-8500/go.mod h1:qmOw/29LhPpII8cDmbTL0OF3btwp97ss7nFcQz72NDM= +github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4 v4.0.1-beta.1 h1:hvy3QCc2SgVidYxTq0rRPOazJOt1PP8A86kW7j6sywU= +github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4 v4.0.1-beta.1/go.mod h1:Yhk+xD4mN90OKEHnk5ARf97CX5p4+MEC/B/YIVoZeZ0= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= diff --git a/nutanix/provider/provider.go b/nutanix/provider/provider.go index 15b417e66..d25f0d1cf 100644 --- a/nutanix/provider/provider.go +++ b/nutanix/provider/provider.go @@ -19,6 +19,7 @@ import ( "github.com/terraform-providers/terraform-provider-nutanix/nutanix/services/v2/networkingv2" "github.com/terraform-providers/terraform-provider-nutanix/nutanix/services/v2/iamv2" "github.com/terraform-providers/terraform-provider-nutanix/nutanix/services/v2/storagecontainersv2" + "github.com/terraform-providers/terraform-provider-nutanix/nutanix/services/v2/prismv2" ) var requiredProviderFields map[string][]string = map[string][]string{ @@ -255,6 +256,8 @@ func Provider() *schema.Provider { "nutanix_storage_container_v2": storagecontainersv2.DatasourceNutanixStorageContainerV2(), "nutanix_storage_containers_v2": storagecontainersv2.DatasourceNutanixStorageContainersV2(), "nutanix_storage_container_stats_info_v2": storagecontainersv2.DatasourceNutanixStorageStatsInfoV2(), + "nutanix_category_v2": prismv2.DatasourceNutanixCategoryV2(), + "nutanix_categories_v2": prismv2.DatasourceNutanixCategoriesV2(), }, ResourcesMap: map[string]*schema.Resource{ "nutanix_virtual_machine": prism.ResourceNutanixVirtualMachine(), @@ -321,6 +324,7 @@ func Provider() *schema.Provider { "nutanix_authorization_policy_v2": iamv2.ResourceNutanixAuthPoliciesV2(), "nutanix_saml_identity_providers_v2": iamv2.ResourceNutanixSamlIdpV2(), "nutanix_storage_containers_v2": storagecontainersv2.ResourceNutanixStorageContainersV2(), + "nutanix_category_v2": prismv2.ResourceNutanixCategoriesV2(), }, ConfigureContextFunc: providerConfigure, } diff --git a/nutanix/sdks/v4/prism/prism.go b/nutanix/sdks/v4/prism/prism.go index ef6511bca..bc6341530 100644 --- a/nutanix/sdks/v4/prism/prism.go +++ b/nutanix/sdks/v4/prism/prism.go @@ -30,7 +30,7 @@ func NewPrismClient(credentials client.Credentials) (*Client, error) { } f := &Client{ - TaskRefAPI: api.NewTasksApi(baseClient), + TaskRefAPI: api.NewTasksApi(baseClient), CategoriesAPIInstance: api.NewCategoriesApi(baseClient), } diff --git a/nutanix/services/v2/prismv2/data_source_nutanix_categories_v2.go b/nutanix/services/v2/prismv2/data_source_nutanix_categories_v2.go new file mode 100644 index 000000000..506e067e7 --- /dev/null +++ b/nutanix/services/v2/prismv2/data_source_nutanix_categories_v2.go @@ -0,0 +1,225 @@ +package prismv2 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4/models/prism/v4/config" + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixCategoriesV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixCategoriesV2Read, + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Optional: true, + }, + "limit": { + Type: schema.TypeInt, + Optional: true, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + }, + "order_by": { + Type: schema.TypeString, + Optional: true, + }, + "expand": { + Type: schema.TypeString, + Optional: true, + }, + "select": { + Type: schema.TypeString, + Optional: true, + }, + "categories": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Computed: true, + }, + "key": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "owner_uuid": { + Type: schema.TypeString, + Computed: true, + }, + "associations": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + }, + "resource_group": { + Type: schema.TypeString, + Computed: true, + }, + "count": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "detailed_associations": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + }, + "resource_group": { + Type: schema.TypeString, + Computed: true, + }, + "resource_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + "links": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rel": { + Type: schema.TypeString, + Computed: true, + }, + "href": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func DatasourceNutanixCategoriesV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).PrismAPI + + // initialize query params + var filter, orderBy, expand, selects *string + var page, limit *int + + if pagef, ok := d.GetOk("page"); ok { + page = utils.IntPtr(pagef.(int)) + } else { + page = nil + } + if limitf, ok := d.GetOk("limit"); ok { + limit = utils.IntPtr(limitf.(int)) + } else { + limit = nil + } + if filterf, ok := d.GetOk("filter"); ok { + filter = utils.StringPtr(filterf.(string)) + } else { + filter = nil + } + if order, ok := d.GetOk("order_by"); ok { + orderBy = utils.StringPtr(order.(string)) + } else { + orderBy = nil + } + if expandf, ok := d.GetOk("expand"); ok { + expand = utils.StringPtr(expandf.(string)) + } else { + expand = nil + } + if selectf, ok := d.GetOk("select"); ok { + selects = utils.StringPtr(selectf.(string)) + } else { + selects = nil + } + resp, err := conn.CategoriesAPIInstance.ListCategories(page, limit, filter, orderBy, expand, selects) + if err != nil { + return diag.Errorf("error while fetching categories : %v", err) + } + + checkResp := resp.Data + + if checkResp != nil { + getResp := resp.Data.GetValue().([]import1.Category) + + if err := d.Set("categories", flattenCategoriesEntities(getResp)); err != nil { + return diag.FromErr(err) + } + } + + d.SetId(resource.UniqueId()) + return nil +} + +func flattenCategoriesEntities(pr []import1.Category) []interface{} { + if len(pr) > 0 { + ctgList := make([]interface{}, len(pr)) + + for k, v := range pr { + ctg := make(map[string]interface{}) + + ctg["ext_id"] = v.ExtId + ctg["key"] = v.Key + ctg["value"] = v.Value + ctg["type"] = flattenCategoryType(v.Type) + ctg["description"] = v.Description + ctg["owner_uuid"] = v.OwnerUuid + ctg["associations"] = flattenAssociationSummary(v.Associations) + ctg["detailed_associations"] = flattenAssociationDetail(v.DetailedAssociations) + ctg["tenant_id"] = v.TenantId + ctg["links"] = flattenLinks(v.Links) + + ctgList[k] = ctg + } + return ctgList + } + return nil +} diff --git a/nutanix/services/v2/prismv2/data_source_nutanix_categories_v2_test.go b/nutanix/services/v2/prismv2/data_source_nutanix_categories_v2_test.go new file mode 100644 index 000000000..78c9b122c --- /dev/null +++ b/nutanix/services/v2/prismv2/data_source_nutanix_categories_v2_test.go @@ -0,0 +1,100 @@ +package prismv2_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameCatgs = "data.nutanix_categories_v2.test" + +func TestAccNutanixCategoriesDataSourceV2_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCategoriesDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.#"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.key"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.value"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.type"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.associations.#"), + ), + }, + }, + }) +} + +func TestAccNutanixCategoriesDataSourceV2_WithFilter(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCategoriesDataSourceConfigWithFilter(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.#"), + resource.TestCheckResourceAttr(datasourceNameCatgs, "categories.#", "1"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.key"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.value"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.type"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.associations.#"), + ), + }, + }, + }) +} + +func TestAccNutanixCategoriesDataSourceV2_WithLimit(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCategoriesDataSourceConfigWithLimit(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.#"), + resource.TestCheckResourceAttr(datasourceNameCatgs, "categories.#", "2"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.key"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.value"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.type"), + resource.TestCheckResourceAttrSet(datasourceNameCatgs, "categories.0.associations.#"), + ), + }, + }, + }) +} + +func testAccCategoriesDataSourceConfig() string { + return (` + data "nutanix_categories_v2" "test" { } + `) +} + +func testAccCategoriesDataSourceConfigWithFilter() string { + return (` + + data "nutanix_categories_v2" "dtest" { } + + locals{ + kk = data.nutanix_categories_v2.dtest.categories.0.key + } + data "nutanix_categories_v2" "test" { + filter = "key eq '${local.kk}'" + depends_on = [ + data.nutanix_categories_v2.dtest + ] + } + `) +} + +func testAccCategoriesDataSourceConfigWithLimit() string { + return (` + data "nutanix_categories_v2" "test" { + limit = 2 + } + `) +} diff --git a/nutanix/services/v2/prismv2/data_source_nutanix_category_v2.go b/nutanix/services/v2/prismv2/data_source_nutanix_category_v2.go new file mode 100644 index 000000000..1c64de793 --- /dev/null +++ b/nutanix/services/v2/prismv2/data_source_nutanix_category_v2.go @@ -0,0 +1,339 @@ +package prismv2 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import2 "github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4/models/common/v1/response" + import1 "github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4/models/prism/v4/config" + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixCategoryV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixCategoryV2Read, + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Required: true, + }, + "expand": { + Type: schema.TypeString, + Optional: true, + }, + "key": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "owner_uuid": { + Type: schema.TypeString, + Computed: true, + }, + "associations": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + }, + "resource_group": { + Type: schema.TypeString, + Computed: true, + }, + "count": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "detailed_associations": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + }, + "resource_group": { + Type: schema.TypeString, + Computed: true, + }, + "resource_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + "links": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rel": { + Type: schema.TypeString, + Computed: true, + }, + "href": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DatasourceNutanixCategoryV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).PrismAPI + + extID := d.Get("ext_id") + var expand *string + if expandf, ok := d.GetOk("expand"); ok { + expand = utils.StringPtr(expandf.(string)) + } else { + expand = nil + } + resp, err := conn.CategoriesAPIInstance.GetCategoryById(utils.StringPtr(extID.(string)), expand) + if err != nil { + return diag.Errorf("error while fetching category : %v", err) + } + + getResp := resp.Data.GetValue().(import1.Category) + + if err := d.Set("key", getResp.Key); err != nil { + return diag.FromErr(err) + } + if err := d.Set("value", getResp.Value); err != nil { + return diag.FromErr(err) + } + if err := d.Set("type", flattenCategoryType(getResp.Type)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("description", getResp.Description); err != nil { + return diag.FromErr(err) + } + if err := d.Set("owner_uuid", getResp.OwnerUuid); err != nil { + return diag.FromErr(err) + } + if err := d.Set("associations", flattenAssociationSummary(getResp.Associations)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("detailed_associations", flattenAssociationDetail(getResp.DetailedAssociations)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("tenant_id", getResp.TenantId); err != nil { + return diag.FromErr(err) + } + if err := d.Set("links", flattenLinks(getResp.Links)); err != nil { + return diag.FromErr(err) + } + + d.SetId(*getResp.ExtId) + return nil +} + +func flattenCategoryType(pr *import1.CategoryType) string { + const two, three, four = 2, 3, 4 + if pr != nil { + if *pr == import1.CategoryType(two) { + return "USER" + } + if *pr == import1.CategoryType(three) { + return "SYSTEM" + } + if *pr == import1.CategoryType(four) { + return "INTERNAL" + } + } + return "UNKNOWN" +} + +func flattenAssociationSummary(pr []import1.AssociationSummary) []interface{} { + if len(pr) > 0 { + associationList := make([]interface{}, len(pr)) + + for k, v := range pr { + assn := make(map[string]interface{}) + + assn["category_id"] = v.CategoryId + assn["count"] = v.Count + assn["resource_group"] = flattenResourceGroup(v.ResourceGroup) + assn["resource_type"] = flattenResourceType(v.ResourceType) + + associationList[k] = assn + } + return associationList + } + return nil +} + +func flattenAssociationDetail(pr []import1.AssociationDetail) []interface{} { + if len(pr) > 0 { + detailList := make([]interface{}, len(pr)) + + for k, v := range pr { + detail := make(map[string]interface{}) + + detail["category_id"] = v.CategoryId + detail["resource_group"] = flattenResourceGroup(v.ResourceGroup) + detail["resource_type"] = flattenResourceType(v.ResourceType) + detail["resource_id"] = v.ResourceId + + detailList[k] = detail + } + return detailList + } + return nil +} + +func flattenResourceGroup(pr *import1.ResourceGroup) string { + const two, three = 2, 3 + if pr != nil { + if *pr == import1.ResourceGroup(two) { + return "ENTITY" + } + if *pr == import1.ResourceGroup(three) { + return "POLICY" + } + } + return "UNKNOWN" +} + +func flattenResourceType(pr *import1.ResourceType) string { + const ( + two, three, four, five, six, seven, eight, nine, ten, eleven, twelve, thirteen, fourteen, fifteen, + sixteen, seventeen, eighteen, nineteen, twenty, twentyone, twentytwo, twentythree, twentyfour, twentyfive, + twentysix = 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 + ) + if pr != nil { + if *pr == import1.ResourceType(two) { + return "VM" + } + if *pr == import1.ResourceType(three) { + return "MH_VM" + } + if *pr == import1.ResourceType(four) { + return "IMAGE" + } + if *pr == import1.ResourceType(five) { + return "SUBNET" + } + if *pr == import1.ResourceType(six) { + return "CLUSTER" + } + if *pr == import1.ResourceType(seven) { + return "HOST" + } + if *pr == import1.ResourceType(eight) { + return "REPORT" + } + if *pr == import1.ResourceType(nine) { + return "MARKETPLACE_ITEM" + } + if *pr == import1.ResourceType(ten) { + return "BLUEPRINT" + } + if *pr == import1.ResourceType(eleven) { + return "APP" + } + if *pr == import1.ResourceType(twelve) { + return "VOLUMEGROUP" + } + if *pr == import1.ResourceType(thirteen) { + return "IMAGE_PLACEMENT_POLICY" + } + if *pr == import1.ResourceType(fourteen) { + return "NETWORK_SECURITY_POLICY" + } + if *pr == import1.ResourceType(fifteen) { + return "NETWORK_SECURITY_RULE" + } + if *pr == import1.ResourceType(sixteen) { + return "VM_HOST_AFFINITY_POLICY" + } + if *pr == import1.ResourceType(seventeen) { + return "VM_VM_ANTI_AFFINITY_POLICY" + } + if *pr == import1.ResourceType(eighteen) { + return "QOS_POLICY" + } + if *pr == import1.ResourceType(nineteen) { + return "NGT_POLICY" + } + if *pr == import1.ResourceType(twenty) { + return "PROTECTION_RULE" + } + if *pr == import1.ResourceType(twentyone) { + return "ACCESS_CONTROL_POLICY" + } + if *pr == import1.ResourceType(twentytwo) { + return "STORAGE_POLICY" + } + if *pr == import1.ResourceType(twentythree) { + return "IMAGE_RATE_LIMIT" + } + if *pr == import1.ResourceType(twentyfour) { + return "RECOVERY_PLAN" + } + if *pr == import1.ResourceType(twentyfive) { + return "BUNDLE" + } + if *pr == import1.ResourceType(twentysix) { + return "POLICY_SCHEMA" + } + } + return "UNKNOWN" +} + +func flattenLinks(pr []import2.ApiLink) []map[string]interface{} { + if len(pr) > 0 { + linkList := make([]map[string]interface{}, len(pr)) + + for k, v := range pr { + links := map[string]interface{}{} + if v.Href != nil { + links["href"] = v.Href + } + if v.Rel != nil { + links["rel"] = v.Rel + } + + linkList[k] = links + } + return linkList + } + return nil +} diff --git a/nutanix/services/v2/prismv2/data_source_nutanix_category_v2_test.go b/nutanix/services/v2/prismv2/data_source_nutanix_category_v2_test.go new file mode 100644 index 000000000..33031a04a --- /dev/null +++ b/nutanix/services/v2/prismv2/data_source_nutanix_category_v2_test.go @@ -0,0 +1,44 @@ +package prismv2_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameCatg = "data.nutanix_category_v2.test" + +func TestAccNutanixCategoryDataSourceV2_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCategoryDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameCatg, "description"), + resource.TestCheckResourceAttrSet(datasourceNameCatg, "key"), + resource.TestCheckResourceAttrSet(datasourceNameCatg, "value"), + resource.TestCheckResourceAttrSet(datasourceNameCatg, "type"), + resource.TestCheckResourceAttrSet(datasourceNameCatg, "associations.#"), + resource.TestCheckResourceAttrSet(datasourceNameCatg, "detailed_associations.#"), + ), + }, + }, + }) +} + +func testAccCategoryDataSourceConfig() string { + return (` + data "nutanix_categories_v2" "dtest" { } + + data "nutanix_category_v2" "test" { + ext_id = data.nutanix_categories_v2.dtest.categories.0.ext_id + + depends_on = [ + data.nutanix_categories_v2.dtest + ] + } + `) +} diff --git a/nutanix/services/v2/prismv2/resource_nutanix_categories_v2.go b/nutanix/services/v2/prismv2/resource_nutanix_categories_v2.go new file mode 100644 index 000000000..0df4331cb --- /dev/null +++ b/nutanix/services/v2/prismv2/resource_nutanix_categories_v2.go @@ -0,0 +1,234 @@ +package prismv2 + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + import1 "github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4/models/prism/v4/config" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func ResourceNutanixCategoriesV2() *schema.Resource { + return &schema.Resource{ + CreateContext: ResourceNutanixCategoriesV2Create, + ReadContext: ResourceNutanixCategoriesV2Read, + UpdateContext: ResourceNutanixCategoriesV2Update, + DeleteContext: ResourceNutanixCategoriesV2Delete, + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"USER", "INTERNAL", "SYSTEM"}, false), + }, + "description": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "owner_uuid": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "associations": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + }, + "resource_group": { + Type: schema.TypeString, + Computed: true, + }, + "count": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "detailed_associations": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + }, + "resource_group": { + Type: schema.TypeString, + Computed: true, + }, + "resource_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func ResourceNutanixCategoriesV2Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).PrismAPI + + input := &import1.Category{} + + if key, ok := d.GetOk("key"); ok { + input.Key = utils.StringPtr(key.(string)) + } + if val, ok := d.GetOk("value"); ok { + input.Value = utils.StringPtr(val.(string)) + } + if types, ok := d.GetOk("type"); ok { + const two, three, four = 2, 3, 4 + subMap := map[string]interface{}{ + "USER": two, + "SYSTEM": three, + "INTERNAL": four, + } + + pInt := subMap[types.(string)] + p := import1.CategoryType(pInt.(int)) + + input.Type = &p + } + if desc, ok := d.GetOk("description"); ok { + input.Description = utils.StringPtr(desc.(string)) + } + if ownerUUID, ok := d.GetOk("owner_uuid"); ok { + input.OwnerUuid = utils.StringPtr(ownerUUID.(string)) + } + + resp, err := conn.CategoriesAPIInstance.CreateCategory(input) + if err != nil { + return diag.Errorf("error while creating category: %v", err) + } + + getResp := resp.Data.GetValue().(import1.Category) + + d.SetId(*getResp.ExtId) + return ResourceNutanixCategoriesV2Read(ctx, d, meta) +} + +func ResourceNutanixCategoriesV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).PrismAPI + + resp, err := conn.CategoriesAPIInstance.GetCategoryById(utils.StringPtr(d.Id()), nil) + if err != nil { + return diag.Errorf("error while fetching category : %v", err) + } + + getResp := resp.Data.GetValue().(import1.Category) + + if err := d.Set("key", getResp.Key); err != nil { + return diag.FromErr(err) + } + if err := d.Set("value", getResp.Value); err != nil { + return diag.FromErr(err) + } + if err := d.Set("type", flattenCategoryType(getResp.Type)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("description", getResp.Description); err != nil { + return diag.FromErr(err) + } + if err := d.Set("owner_uuid", getResp.OwnerUuid); err != nil { + return diag.FromErr(err) + } + if err := d.Set("associations", flattenAssociationSummary(getResp.Associations)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("detailed_associations", flattenAssociationDetail(getResp.DetailedAssociations)); err != nil { + return diag.FromErr(err) + } + return nil +} + +func ResourceNutanixCategoriesV2Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).PrismAPI + updatedInput := import1.Category{} + resp, err := conn.CategoriesAPIInstance.GetCategoryById(utils.StringPtr(d.Id()), nil) + + if err != nil { + return diag.Errorf("error while fetching categories : %v", err) + } + + updatedInput = resp.Data.GetValue().(import1.Category) + + if d.HasChange("value") { + updatedInput.Value = utils.StringPtr(d.Get("value").(string)) + } + if d.HasChange("description") { + updatedInput.Description = utils.StringPtr(d.Get("description").(string)) + } + if d.HasChange("type") { + const two, three, four = 2, 3, 4 + subMap := map[string]interface{}{ + "USER": two, + "SYSTEM": three, + "INTERNAL": four, + } + + pInt := subMap[d.Get("type").(string)] + p := import1.CategoryType(pInt.(int)) + updatedInput.Type = &p + } + if d.HasChange("owner_uuid") { + updatedInput.OwnerUuid = utils.StringPtr(d.Get("owner_uuid").(string)) + } + + upResp, er := conn.CategoriesAPIInstance.UpdateCategoryById(utils.StringPtr(d.Id()), &updatedInput) + if er != nil { + return diag.Errorf("error while updating categories : %v", err) + } + + updatedRes := upResp.Data.GetValue().(import1.Category) + + if updatedRes.ExtId != nil { + fmt.Println("Category updated successfully") + } + return ResourceNutanixCategoriesV2Read(ctx, d, meta) +} + +func ResourceNutanixCategoriesV2Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).PrismAPI + + resp, err := conn.CategoriesAPIInstance.DeleteCategoryById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while deleting category : %v", err) + } + + if resp == nil { + fmt.Println("Category deleted successfully.") + } + + return nil +} diff --git a/nutanix/services/v2/prismv2/resource_nutanix_categories_v2_test.go b/nutanix/services/v2/prismv2/resource_nutanix_categories_v2_test.go new file mode 100644 index 000000000..7d4e8bb25 --- /dev/null +++ b/nutanix/services/v2/prismv2/resource_nutanix_categories_v2_test.go @@ -0,0 +1,111 @@ +package prismv2_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const resourceNameCategory = "nutanix_category_v2.test" + +func TestAccNutanixCategoryV2_Basic(t *testing.T) { + r := acctest.RandInt() + value := fmt.Sprintf("test category value-%d", r) + desc := "test category description" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCategoryV2Config(r, value, desc), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceNameCategory, "key", fmt.Sprintf("test-cat-%d", r)), + resource.TestCheckResourceAttr(resourceNameCategory, "value", value), + resource.TestCheckResourceAttr(resourceNameCategory, "description", desc), + resource.TestCheckResourceAttr(resourceNameCategory, "type", "USER"), + ), + }, + }, + }) +} + +func TestAccNutanixCategoryV2_Update(t *testing.T) { + r := acctest.RandInt() + value := fmt.Sprintf("test category value-%d", r) + updatedValue := fmt.Sprintf("test category value updated-%d", r) + desc := "test category description" + updateDesc := "test category description updated" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCategoryV2Config(r, value, desc), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceNameCategory, "key", fmt.Sprintf("test-cat-%d", r)), + resource.TestCheckResourceAttr(resourceNameCategory, "value", value), + resource.TestCheckResourceAttr(resourceNameCategory, "description", desc), + resource.TestCheckResourceAttr(resourceNameCategory, "type", "USER"), + ), + }, + { + Config: testAccCategoryV2ConfigUpdated(r, updatedValue, updateDesc), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceNameCategory, "key", fmt.Sprintf("test-cat-%d", r)), + resource.TestCheckResourceAttr(resourceNameCategory, "value", updatedValue), + resource.TestCheckResourceAttr(resourceNameCategory, "description", updateDesc), + resource.TestCheckResourceAttr(resourceNameCategory, "type", "USER"), + ), + }, + }, + }) +} + +func TestAccNutanixCategoryV2_WithNoKey(t *testing.T) { + r := acctest.RandInt() + value := fmt.Sprintf("test category value-%d", r) + desc := "test category description" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCategoryV2ConfigWithNoKey(r, value, desc), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func testAccCategoryV2Config(r int, val, desc string) string { + return fmt.Sprintf(` + resource "nutanix_category_v2" "test" { + key = "test-cat-%d" + value = "%[2]s" + description = "%[3]s" + } +`, r, val, desc) +} + +func testAccCategoryV2ConfigUpdated(r int, val, desc string) string { + return fmt.Sprintf(` + resource "nutanix_category_v2" "test" { + key = "test-cat-%d" + value = "%[2]s" + description = "%[3]s" + } +`, r, val, desc) +} + +func testAccCategoryV2ConfigWithNoKey(r int, val, desc string) string { + return fmt.Sprintf(` + resource "nutanix_category_v2" "test" { + value = "%[2]s" + description = "%[3]s" + } +`, r, val, desc) +} diff --git a/test_config_v4.json b/test_config_v4.json new file mode 100644 index 000000000..028c10e7b --- /dev/null +++ b/test_config_v4.json @@ -0,0 +1,16 @@ +{ + "auth_policies": { + "limit": 1, + "role": "00000000-0000-0000-0000-000000000000", + "display_name": "auth_policies_test", + "description": "auth policies description ", + "description_update": "auth policies description test", + "authorization_policy_type": "USER_DEFINED", + "identities": [ + "{\"user\":{\"uuid\":{\"anyof\":[\"00000000-0000-0000-0000-000000000000\"]}}}" + ], + "entities": [ + "{\"*\":{\"*\":{\"eq\":\"*\"}}}" + ] + } +} \ No newline at end of file diff --git a/website/docs/d/categories_v2.html.markdown b/website/docs/d/categories_v2.html.markdown new file mode 100644 index 000000000..478b30702 --- /dev/null +++ b/website/docs/d/categories_v2.html.markdown @@ -0,0 +1,69 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_categories_v2" +sidebar_current: "docs-nutanix-datasource-categories-v4" +description: |- + Fetch a list of categories with pagination, filtering, sorting, selection and optional expansion of associated entity counts. +--- + +# nutanix_categories_v2 +List categories + + +## Example + +```hcl + + data "nutanix_categories_v2" "test" { } + + data "nutanix_categories_v2" "test2" { + filter = "key eq '{}'" + } + +``` + + +## Argument Reference + +The following arguments are supported: + +* `page`: (Optional) A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results. +* `limit`: (Optional) A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set. +* `filter`: (Optional) A URL query parameter that allows clients to filter a collection of resources. +* `order_by`: (Optional) A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default +* `expand`: (Optional) A URL query parameter that allows clients to request related resources when a resource that satisfies a particular request is retrieved. +* `select`: (Optional) A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. + +* `categories`: List of categories + +## categories + +* `ext_id`: The extID for the category. +* `key`: The key of a category when it is represented in key:value format. +* `value`: The value of a category when it is represented in key:value format +* `type`: Denotes the type of a category. +There are three types of categories: SYSTEM, INTERNAL, and USER. +* `description`: A string consisting of the description of the category as defined by the user. +* `owner_uuid`: This field contains the UUID of a user who owns the category. +* `associations`: This field gives basic information about resources that are associated to the category. +* `detailed_associations`: This field gives detailed information about resources that are associated to the category. +* `tenant_id`: A globally unique identifier that represents the tenant that owns this entity. +* `links`: A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. + + +### associations +* `category_id`: External identifier for the given category, used across all v4 apis/entities/resources where categories are referenced. +* `resource_type`: An enum denoting the associated resource types. Resource types are further grouped into 2 types - entity or a policy. +* `resource_group`: An enum denoting the resource group. +Resources can be organised into either an entity or a policy. +* `count`: Count of associations of a particular type of entity or policy + +### detailed_associations +* `category_id`: External identifier for the given category, used across all v4 apis/entities/resources where categories are referenced. +* `resource_type`: An enum denoting the associated resource types. Resource types are further grouped into 2 types - entity or a policy. +* `resource_group`: An enum denoting the resource group. +Resources can be organised into either an entity or a policy. +* `resource_id`: The UUID of the entity or policy associated with the particular category. + + +See detailed information in [Nutanix Categories v4](https://developers.nutanix.com/api-reference?namespace=prism&version=v4.0.b1). \ No newline at end of file diff --git a/website/docs/d/category_v2.html.markdown b/website/docs/d/category_v2.html.markdown new file mode 100644 index 000000000..6a81cf7fb --- /dev/null +++ b/website/docs/d/category_v2.html.markdown @@ -0,0 +1,62 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_category_v2" +sidebar_current: "docs-nutanix-datasource-category-v4" +description: |- + Fetch details of a category with the given external identifier. +--- + +# nutanix_category_v2 +Fetch a category + + +## Example + +```hcl + + data "nutanix_category_v2" "test" { + ext_id = {{ ext_id of category}} + } + +``` + + +## Argument Reference + +The following arguments are supported: + +* `ext_id`: (Required) The extID for the category. +* `expand`: (Optional) A URL query parameter that allows clients to request related resources when a resource that satisfies a particular request is retrieved. + +## Attributes Reference + +The following attributes are exported: + +* `key`: The key of a category when it is represented in key:value format. +* `value`: The value of a category when it is represented in key:value format +* `type`: Denotes the type of a category. +There are three types of categories: SYSTEM, INTERNAL, and USER. +* `description`: A string consisting of the description of the category as defined by the user. +* `owner_uuid`: This field contains the UUID of a user who owns the category. +* `associations`: This field gives basic information about resources that are associated to the category. +* `detailed_associations`: This field gives detailed information about resources that are associated to the category. +* `tenant_id`: A globally unique identifier that represents the tenant that owns this entity. +* `links`: A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. + + +### associations +* `category_id`: External identifier for the given category, used across all v4 apis/entities/resources where categories are referenced. +* `resource_type`: An enum denoting the associated resource types. Resource types are further grouped into 2 types - entity or a policy. +* `resource_group`: An enum denoting the resource group. +Resources can be organised into either an entity or a policy. +* `count`: Count of associations of a particular type of entity or policy + +### detailed_associations +* `category_id`: External identifier for the given category, used across all v4 apis/entities/resources where categories are referenced. +* `resource_type`: An enum denoting the associated resource types. Resource types are further grouped into 2 types - entity or a policy. +* `resource_group`: An enum denoting the resource group. +Resources can be organised into either an entity or a policy. +* `resource_id`: The UUID of the entity or policy associated with the particular category. + + +See detailed information in [Nutanix Category v4](https://developers.nutanix.com/api-reference?namespace=prism&version=v4.0.b1). \ No newline at end of file