diff --git a/client/calm/calm.go b/client/calm/calm.go new file mode 100644 index 000000000..377aab89c --- /dev/null +++ b/client/calm/calm.go @@ -0,0 +1,49 @@ +package calm + +import ( + "fmt" + "strings" + + "github.com/terraform-providers/terraform-provider-nutanix/client" +) + +const ( + libraryVersion = "v3.0" + absolutePath = "api/calm/" + libraryVersion + userAgent = "nutanix/" + libraryVersion + clientName = "calm" +) + +// Client manages the Calm API +type Client struct { + client *client.Client + V3 Service +} + +// NewV3Client return a client to operate V3 resources +func NewV3Client(credentials client.Credentials) (*Client, error) { + var baseClient *client.Client + + // check if all required fields are present. Else create an empty client + if credentials.Username != "" && credentials.Password != "" && credentials.Endpoint != "" { + c, err := client.NewClient(&credentials, userAgent, absolutePath, false) + if err != nil { + return nil, err + } + baseClient = c + } else { + errorMsg := fmt.Sprintf("Prism Central (PC) Client is missing. "+ + "Please provide required details - %s in provider configuration.", strings.Join(credentials.RequiredFields[clientName], ", ")) + + baseClient = &client.Client{UserAgent: userAgent, ErrorMsg: errorMsg} + } + + f := &Client{ + client: baseClient, + V3: Operations{ + client: baseClient, + }, + } + + return f, nil +} diff --git a/client/calm/calm_service.go b/client/calm/calm_service.go new file mode 100644 index 000000000..e35597393 --- /dev/null +++ b/client/calm/calm_service.go @@ -0,0 +1,54 @@ +package calm + +import ( + "context" + "fmt" + "net/http" + + "github.com/terraform-providers/terraform-provider-nutanix/client" +) + +// Operations implements Service interface +type Operations struct { + client *client.Client +} + +type Service interface { + CreateProjectQuota(ctx context.Context, request *ProjectQuotaIntentInput) (*ProjectQuotaIntentResponse, error) + UpdateProjectQuota(ctx context.Context, quotaID string, request *ProjectQuotaIntentInput) (*ProjectQuotaIntentResponse, error) + EnableProjectQuota(ctx context.Context, request *EnableProjectQuotaInput) (*ProjectQuotaIntentResponse, error) +} + +func (op Operations) CreateProjectQuota(ctx context.Context, request *ProjectQuotaIntentInput) (*ProjectQuotaIntentResponse, error) { + req, err := op.client.NewRequest(ctx, http.MethodPost, "/quotas", request) + if err != nil { + return nil, err + } + + projectResponse := new(ProjectQuotaIntentResponse) + + return projectResponse, op.client.Do(ctx, req, projectResponse) +} + +func (op Operations) UpdateProjectQuota(ctx context.Context, quotaID string, request *ProjectQuotaIntentInput) (*ProjectQuotaIntentResponse, error) { + path := fmt.Sprintf("/quotas/%s", quotaID) + req, err := op.client.NewRequest(ctx, http.MethodPut, path, request) + if err != nil { + return nil, err + } + + projectResponse := new(ProjectQuotaIntentResponse) + + return projectResponse, op.client.Do(ctx, req, projectResponse) +} + +func (op Operations) EnableProjectQuota(ctx context.Context, request *EnableProjectQuotaInput) (*ProjectQuotaIntentResponse, error) { + req, err := op.client.NewRequest(ctx, http.MethodPut, "/quotas/update/state", request) + if err != nil { + return nil, err + } + + quotaEnableResponse := new(ProjectQuotaIntentResponse) + + return quotaEnableResponse, op.client.Do(ctx, req, quotaEnableResponse) +} diff --git a/client/calm/calm_structs.go b/client/calm/calm_structs.go new file mode 100644 index 000000000..d9a1e0656 --- /dev/null +++ b/client/calm/calm_structs.go @@ -0,0 +1,96 @@ +package calm + +import "time" + +// Reference ... +type Reference struct { + Kind *string `json:"kind" mapstructure:"kind"` + Name *string `json:"name,omitempty" mapstructure:"name,omitempty"` + UUID *string `json:"uuid" mapstructure:"uuid"` +} + +// Metadata Metadata The kind metadata +type Metadata struct { + LastUpdateTime *time.Time `json:"last_update_time,omitempty" mapstructure:"last_update_time,omitempty"` // + Kind *string `json:"kind" mapstructure:"kind"` // + UUID *string `json:"uuid,omitempty" mapstructure:"uuid,omitempty"` // + ProjectReference *Reference `json:"project_reference,omitempty" mapstructure:"project_reference,omitempty"` // project reference + CreationTime *time.Time `json:"creation_time,omitempty" mapstructure:"creation_time,omitempty"` + SpecVersion *int64 `json:"spec_version,omitempty" mapstructure:"spec_version,omitempty"` + SpecHash *string `json:"spec_hash,omitempty" mapstructure:"spec_hash,omitempty"` + OwnerReference *Reference `json:"owner_reference,omitempty" mapstructure:"owner_reference,omitempty"` + Categories map[string]string `json:"categories,omitempty" mapstructure:"categories,omitempty"` + Name *string `json:"name,omitempty" mapstructure:"name,omitempty"` + + // Applied on Prism Central only. Indicate whether force to translate the spec of the fanout request to fit the target cluster API schema. + ShouldForceTranslate *bool `json:"should_force_translate,omitempty" mapstructure:"should_force_translate,omitempty"` + + //TODO: add if necessary + //CategoriesMapping map[string][]string `json:"categories_mapping,omitempty" mapstructure:"categories_mapping,omitempty"` + //EntityVersion *string `json:"entity_version,omitempty" mapstructure:"entity_version,omitempty"` + //UseCategoriesMapping *bool `json:"use_categories_mapping,omitempty" mapstructure:"use_categories_mapping,omitempty"` +} + +// MessageResource ... +type MessageResource struct { + + // Custom key-value details relevant to the status. + Details map[string]string `json:"details,omitempty" mapstructure:"details,omitempty"` + + // If state is ERROR, a message describing the error. + Message *string `json:"message" mapstructure:"message"` + + // If state is ERROR, a machine-readable snake-cased *string. + Reason *string `json:"reason" mapstructure:"reason"` +} + +type ProjectQuotaMetadata struct { + Kind *string `json:"kind,omitempty"` + ProjectReference *Reference `json:"project_reference,omitempty"` + UUID *string `json:"uuid,omitempty"` +} + +type ProjectQuotaData struct { + Disk *int `json:"disk,omitempty"` + VCPU *int `json:"vcpu,omitempty"` + Memory *int `json:"memory,omitempty"` +} + +type ProjectQuotaEntities struct { + Project *string `json:"project,omitempty"` +} + +type ProjectQuotaResources struct { + Data *ProjectQuotaData `json:"data,omitempty"` + Entities *ProjectQuotaEntities `json:"entities,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` + UUID *string `json:"uuid,omitempty"` + State *string `json:"state,omitempty"` + EntityType *string `json:"entity_type,omitempty"` +} + +type ProjectQuotaSpec struct { + Resources *ProjectQuotaResources `json:"resources,omitempty"` +} + +// Project CALM Quota +type ProjectQuotaIntentInput struct { + Metadata *ProjectQuotaMetadata `json:"metadata,omitempty"` + Spec *ProjectQuotaSpec `json:"spec,omitempty"` +} + +type ProjectQuotaStatus struct { + State *string `json:"state,omitempty"` + MessageList []*MessageResource `json:"message_list,omitempty"` + Resources *ProjectQuotaResources `json:"resources,omitempty"` +} + +type ProjectQuotaIntentResponse struct { + Status *ProjectQuotaStatus `json:"status,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` + Spec *ProjectQuotaSpec `json:"spec,omitempty"` +} + +type EnableProjectQuotaInput struct { + Spec *ProjectQuotaSpec `json:"spec,omitempty"` +} diff --git a/examples/foundationCentral/imaged_nodes/main.tf b/examples/foundationCentral/imaged_nodes/main.tf index 722b6de56..d9c6cf29e 100644 --- a/examples/foundationCentral/imaged_nodes/main.tf +++ b/examples/foundationCentral/imaged_nodes/main.tf @@ -26,6 +26,7 @@ output "img1"{ // datasource to Get the details of a single node given its UUID. data "nutanix_foundation_central_imaged_node_details" "imgdet"{ imaged_node_uuid = "" +} output "imgdetails"{ value = data.nutanix_foundation_central_imaged_node_details.imgdet diff --git a/examples/projects/main.tf b/examples/projects/main.tf index 9b6e73144..43b00cc3d 100644 --- a/examples/projects/main.tf +++ b/examples/projects/main.tf @@ -102,4 +102,11 @@ resource "nutanix_project" "testp1" { description= "descripton" } api_version = "3.1" + + # to enable project quotas + project_quota{ + vcpu = 1 + disk = 2147483648 + memory = 2147483648 + } } diff --git a/go.mod b/go.mod index e5b9ab0d0..5de015b1f 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/client9/misspell v0.3.4 github.com/golang/snappy v0.0.1 // indirect github.com/golangci/golangci-lint v1.25.0 + github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/terraform-plugin-go v0.5.0 // indirect github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1 github.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba // indirect diff --git a/go.sum b/go.sum index 7163b4b34..e8c315da6 100644 --- a/go.sum +++ b/go.sum @@ -310,6 +310,8 @@ github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1 github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= diff --git a/nutanix/config.go b/nutanix/config.go index 8fd13fa26..a25b1a897 100644 --- a/nutanix/config.go +++ b/nutanix/config.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/terraform-providers/terraform-provider-nutanix/client" + "github.com/terraform-providers/terraform-provider-nutanix/client/calm" foundation_central "github.com/terraform-providers/terraform-provider-nutanix/client/fc" "github.com/terraform-providers/terraform-provider-nutanix/client/foundation" "github.com/terraform-providers/terraform-provider-nutanix/client/karbon" @@ -60,12 +61,17 @@ func (c *Config) Client() (*Client, error) { if err != nil { return nil, err } + calmClient, err := calm.NewV3Client(configCreds) + if err != nil { + return nil, err + } return &Client{ WaitTimeout: c.WaitTimeout, API: v3Client, KarbonAPI: karbonClient, FoundationClientAPI: foundationClient, FoundationCentral: fcClient, + Calm: calmClient, }, nil } @@ -76,4 +82,5 @@ type Client struct { FoundationClientAPI *foundation.Client WaitTimeout int64 FoundationCentral *foundation_central.Client + Calm *calm.Client } diff --git a/nutanix/resource_nutanix_project.go b/nutanix/resource_nutanix_project.go index 426171c72..df1a53d24 100644 --- a/nutanix/resource_nutanix_project.go +++ b/nutanix/resource_nutanix_project.go @@ -7,11 +7,13 @@ import ( "strings" "time" + randUUID "github.com/hashicorp/go-uuid" "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" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/spf13/cast" + "github.com/terraform-providers/terraform-provider-nutanix/client/calm" v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" "github.com/terraform-providers/terraform-provider-nutanix/utils" ) @@ -743,6 +745,37 @@ func resourceNutanixProject() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "project_quota": { + Type: schema.TypeList, + Optional: true, + RequiredWith: []string{"use_project_internal"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "disk": { + Type: schema.TypeInt, + Optional: true, + }, + "vcpu": { + Type: schema.TypeInt, + Optional: true, + }, + "memory": { + Type: schema.TypeInt, + Optional: true, + }, + "enable": { + Type: schema.TypeString, + Optional: true, + Default: "enabled", + ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false), + }, + }, + }, + }, + "quota_id": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -783,6 +816,82 @@ func resourceNutanixProjectCreate(ctx context.Context, d *schema.ResourceData, m d.SetId(uuid) + // create quotas with calm APIs + + if pq, ok2 := d.GetOk("project_quota"); ok2 { + quotaSpec := &calm.ProjectQuotaIntentInput{} + spec := &calm.ProjectQuotaSpec{} + quotaMeta := &calm.ProjectQuotaMetadata{} + resor := &calm.ProjectQuotaResources{} + + quotaMeta.Kind = utils.StringPtr("quota") + + // get project info + projSpec := &calm.Reference{} + + projSpec.Kind = utils.StringPtr("project") + projSpec.Name = utils.StringPtr(*resp.Spec.ProjectDetail.Name) + projSpec.UUID = utils.StringPtr(uuid) + + quotaMeta.ProjectReference = projSpec + + // random generate uuid + quotaUUID, _ := randUUID.GenerateUUID() + quotaMeta.UUID = utils.StringPtr(quotaUUID) + + resorData := &calm.ProjectQuotaData{} + quotaState := "enabled" + pqData := pq.([]interface{}) + for _, val := range pqData { + v := val.(map[string]interface{}) + if v1, ok1 := v["disk"]; ok1 { + resorData.Disk = utils.IntPtr(v1.(int)) + } + if v1, ok1 := v["memory"]; ok1 { + resorData.Memory = utils.IntPtr(v1.(int)) + } + if v1, ok1 := v["vcpu"]; ok1 { + resorData.VCPU = utils.IntPtr(v1.(int)) + } + if v1, ok1 := v["enable"]; ok1 { + quotaState = v1.(string) + } + } + resor.Data = resorData + + enty := &calm.ProjectQuotaEntities{} + enty.Project = &uuid + resor.Entities = enty + resor.UUID = utils.StringPtr(quotaUUID) + spec.Resources = resor + quotaSpec.Metadata = quotaMeta + quotaSpec.Spec = spec + + // making request + con2 := meta.(*Client).Calm + res, er := con2.V3.CreateProjectQuota(ctx, quotaSpec) + if er != nil { + return diag.FromErr(err) + } + d.Set("quota_id", res.Metadata.UUID) + + // making quota enable request + enableQuotaInput := &calm.EnableProjectQuotaInput{} + + enableQuotaSpec := &calm.ProjectQuotaSpec{} + enableQuotaRes := &calm.ProjectQuotaResources{} + + enableQuotaRes.Entities = enty + enableQuotaRes.State = utils.StringPtr(quotaState) + + enableQuotaSpec.Resources = enableQuotaRes + enableQuotaInput.Spec = enableQuotaSpec + _, err := con2.V3.EnableProjectQuota(ctx, enableQuotaInput) + if err != nil { + return diag.FromErr(err) + } + } + // once project is created , create acp . // check if acp is given in resource if _, ok1 := d.GetOk("acp"); ok1 { @@ -1161,6 +1270,77 @@ func resourceNutanixProjectUpdate(ctx context.Context, d *schema.ResourceData, m uuid = *resp.Metadata.UUID taskUUID = resp.Status.ExecutionContext.TaskUUID.(string) + + // update spec for project Quota + if d.HasChange("project_quota") { + quotaSpec := &calm.ProjectQuotaIntentInput{} + spec := &calm.ProjectQuotaSpec{} + quotaMeta := &calm.ProjectQuotaMetadata{} + resor := &calm.ProjectQuotaResources{} + + // setting Quota Metadata + + quotaMeta.Kind = utils.StringPtr("quota") + quotaMeta.UUID = utils.StringPtr(d.Get("quota_id").(string)) + + resorData := &calm.ProjectQuotaData{} + quotaState := "enabled" + pq := d.Get("project_quota") + pqData := pq.([]interface{}) + for _, val := range pqData { + v := val.(map[string]interface{}) + if v1, ok1 := v["disk"]; ok1 { + resorData.Disk = utils.IntPtr(v1.(int)) + } + if v1, ok1 := v["memory"]; ok1 { + resorData.Memory = utils.IntPtr(v1.(int)) + } + if v1, ok1 := v["vcpu"]; ok1 { + resorData.VCPU = utils.IntPtr(v1.(int)) + } + if v1, ok1 := v["enable"]; ok1 { + quotaState = v1.(string) + } + } + resor.Data = resorData + + // setting entity_type as vm to match the UI spec + resor.EntityType = utils.StringPtr("vm") + // setting project entities + enty := &calm.ProjectQuotaEntities{} + enty.Project = &uuid + resor.Entities = enty + + resor.UUID = utils.StringPtr(d.Get("quota_id").(string)) + + spec.Resources = resor + quotaSpec.Metadata = quotaMeta + quotaSpec.Spec = spec + + quotaID := (d.Get("quota_id").(string)) + // making request + con2 := meta.(*Client).Calm + _, er := con2.V3.UpdateProjectQuota(ctx, quotaID, quotaSpec) + if er != nil { + return diag.FromErr(err) + } + + // making quota enable request + enableQuotaInput := &calm.EnableProjectQuotaInput{} + + enableQuotaSpec := &calm.ProjectQuotaSpec{} + enableQuotaRes := &calm.ProjectQuotaResources{} + + enableQuotaRes.Entities = enty + enableQuotaRes.State = utils.StringPtr(quotaState) + + enableQuotaSpec.Resources = enableQuotaRes + enableQuotaInput.Spec = enableQuotaSpec + _, err := con2.V3.EnableProjectQuota(ctx, enableQuotaInput) + if err != nil { + return diag.FromErr(err) + } + } } else { project, err := conn.V3.GetProject(d.Id()) if err != nil { @@ -1858,3 +2038,12 @@ func checkACPdelete(resp *v3.ProjectInternalIntentResponse, acpList []*v3.Access } return false, nil } + +func SetQuota(ctx context.Context, d *schema.ResourceData) { + // req := &v3.ProjectQuotaIntentInput{} + // spec := &v3.ProjectQuotaSpec{} + // res := &v3.ProjectQuotaResources{} + + // rd := expandResourceDomain(d) + +} diff --git a/nutanix/resource_nutanix_project_test.go b/nutanix/resource_nutanix_project_test.go index 96b0596cc..e155782a7 100644 --- a/nutanix/resource_nutanix_project_test.go +++ b/nutanix/resource_nutanix_project_test.go @@ -236,6 +236,48 @@ func TestAccNutanixProject_withInternalWithACPUserGroup(t *testing.T) { }) } +func TestAccNutanixProject_withProjectQuotaUpdate(t *testing.T) { + resourceName := "nutanix_project.project_test" + subnetName := acctest.RandomWithPrefix("test-subnateName") + name := acctest.RandomWithPrefix("test-project-name-dou") + description := acctest.RandomWithPrefix("test-project-desc-dou") + vcpu := "1" + updatedName := acctest.RandomWithPrefix("test-project-updated") + updateDes := acctest.RandomWithPrefix("test-desc-got-updated") + updatedVCPU := "2" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccNutanixProjectInternalProjectQuotaUpdate(subnetName, name, description, vcpu), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "api_version", "3.1"), + resource.TestCheckResourceAttr(resourceName, "subnet_reference_list.#", "1"), + resource.TestCheckResourceAttr(resourceName, "project_quota.0.vcpu", vcpu), + resource.TestCheckResourceAttr(resourceName, "project_quota.0.memory", "2147483648"), + resource.TestCheckResourceAttr(resourceName, "project_quota.0.disk", "2147483648"), + ), + }, + { + Config: testAccNutanixProjectInternalProjectQuotaUpdate(subnetName, updatedName, updateDes, updatedVCPU), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", updatedName), + resource.TestCheckResourceAttr(resourceName, "description", updateDes), + resource.TestCheckResourceAttr(resourceName, "api_version", "3.1"), + resource.TestCheckResourceAttr(resourceName, "subnet_reference_list.#", "1"), + resource.TestCheckResourceAttr(resourceName, "project_quota.0.vcpu", updatedVCPU), + resource.TestCheckResourceAttr(resourceName, "project_quota.0.memory", "2147483648"), + resource.TestCheckResourceAttr(resourceName, "project_quota.0.disk", "2147483648"), + ), + }, + }, + }) +} + func testAccCheckNutanixProjectImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { rs, ok := s.RootModule().Resources[resourceName] @@ -715,3 +757,60 @@ func testAccNutanixProjectInternalConfigWithACPUserGroup(subnetName, name, descr } `, subnetName, name, description, categoryName, categoryVal, limit, rsType, testVars.Permissions[0].UUID, testVars.UserGroupWithDistinguishedName[3].DistinguishedName) } + +func testAccNutanixProjectInternalProjectQuotaUpdate(subnetName, name, description, vcpu string) string { + return fmt.Sprintf(` + data "nutanix_clusters" "clusters" {} + + locals { + cluster1 = [ + for cluster in data.nutanix_clusters.clusters.entities : + cluster.metadata.uuid if cluster.service_list[0] != "PRISM_CENTRAL" + ][0] + } + + resource "nutanix_subnet" "subnet" { + cluster_uuid = local.cluster1 + name = "%s" + description = "Description of my unit test VLAN" + vlan_id = 31 + subnet_type = "VLAN" + subnet_ip = "10.250.140.0" + default_gateway_ip = "10.250.140.1" + prefix_length = 24 + + dhcp_options = { + boot_file_name = "bootfile" + domain_name = "nutanix" + tftp_server_name = "10.250.140.200" + } + + dhcp_domain_name_server_list = ["8.8.8.8", "4.2.2.2"] + dhcp_domain_search_list = ["terraform.nutanix.com", "terraform.unit.test.com"] + } + + resource "nutanix_project" "project_test" { + name = "%s" + description = "%s" + + default_subnet_reference { + uuid = nutanix_subnet.subnet.metadata.uuid + } + + use_project_internal = true + + api_version = "3.1" + + subnet_reference_list{ + kind="subnet" + uuid=nutanix_subnet.subnet.metadata.uuid + } + + project_quota{ + vcpu= "%s" + disk=2147483648 + memory=2147483648 + } + } + `, subnetName, name, description, vcpu) +} diff --git a/website/docs/r/project.markdown b/website/docs/r/project.markdown index f7d71dd7b..f4ecca950 100644 --- a/website/docs/r/project.markdown +++ b/website/docs/r/project.markdown @@ -93,6 +93,13 @@ resource "nutanix_project" "project_test" { } description= "{{description}}" } + +# to enable projects quotas once policy engine is enabled + project_quota{ + vcpu = 1 + disk = 2147483648 + memory = 2147483648 + } api_version = "3.1" } @@ -288,6 +295,11 @@ The following arguments are supported: * `user_group.#.metadata.uuid` - (Required) UUID of the USER Group * `user_group.#.metadata.Kind` - Kind of the USER Group. +### Project Quota +* `vcpu` - (Optional) The amount of vcpu resource needed +* `memory` - (Optional) The amount of memory resource needed +* `disk` - (Optional) The amount of disk resource needed +* `enable` - (Optional) Enabled or disabled the state of quota . Default is enabled. ## Attributes Reference The following attributes are exported: