diff --git a/mso/datasource_mso_schema_template_anp_epg_contract.go b/mso/datasource_mso_schema_template_anp_epg_contract.go index c2704ae9..e34b4c27 100644 --- a/mso/datasource_mso_schema_template_anp_epg_contract.go +++ b/mso/datasource_mso_schema_template_anp_epg_contract.go @@ -3,10 +3,8 @@ package mso import ( "fmt" "log" - "regexp" "github.com/ciscoecosystem/mso-go-client/client" - "github.com/ciscoecosystem/mso-go-client/models" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) @@ -56,8 +54,12 @@ func dataSourceMSOTemplateAnpEpgContract() *schema.Resource { ValidateFunc: validation.StringLenBetween(1, 1000), }, "relationship_type": &schema.Schema{ - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + // Computed: true, + // Changed to required because relationship_type is an identifier value + // A contract could be added with the same reference but different relationship_type + Required: true, + ValidateFunc: validation.StringLenBetween(1, 1000), }, }), } @@ -75,14 +77,11 @@ func dataSourceMSOTemplateAnpEpgContractRead(d *schema.ResourceData, m interface if err != nil { return err } - count, err := cont.ArrayCount("templates") - if err != nil { - return fmt.Errorf("No Template found") - } + template := d.Get("template_name").(string) - anp := d.Get("anp_name") - epg := d.Get("epg_name") - contract := d.Get("contract_name") + anp := d.Get("anp_name").(string) + epg := d.Get("epg_name").(string) + contract := d.Get("contract_name").(string) contractSchemaId := d.Get("contract_schema_id").(string) if contractSchemaId == "" { contractSchemaId = schemaId @@ -91,73 +90,24 @@ func dataSourceMSOTemplateAnpEpgContractRead(d *schema.ResourceData, m interface if contractTemplateName == "" { contractTemplateName = template } - found := false - for i := 0; i < count && !found; i++ { - tempCont, err := cont.ArrayElement(i, "templates") - if err != nil { - return err - } - currentTemplate := models.StripQuotes(tempCont.S("name").String()) - - if currentTemplate == template { - anpCount, err := tempCont.ArrayCount("anps") - if err != nil { - return fmt.Errorf("Unable to get ANP list") - } - for j := 0; j < anpCount && !found; j++ { - anpCont, err := tempCont.ArrayElement(j, "anps") - if err != nil { - return err - } - currentAnp := models.StripQuotes(anpCont.S("name").String()) - if currentAnp == anp { - epgCount, err := anpCont.ArrayCount("epgs") - if err != nil { - return fmt.Errorf("Unable to get EPG list") - } - for k := 0; k < epgCount && !found; k++ { - epgCont, err := anpCont.ArrayElement(k, "epgs") - if err != nil { - return err - } - currentEpg := models.StripQuotes(epgCont.S("name").String()) - if currentEpg == epg { - crefCount, err := epgCont.ArrayCount("contractRelationships") - if err != nil { - return fmt.Errorf("Unable to get the contract relationships list") - } - for l := 0; l < crefCount; l++ { - crefCont, err := epgCont.ArrayElement(l, "contractRelationships") - if err != nil { - return err - } - contractRef := models.StripQuotes(crefCont.S("contractRef").String()) - re := regexp.MustCompile("/schemas/(.*)/templates/(.*)/contracts/(.*)") - match := re.FindStringSubmatch(contractRef) - if match[3] == contract && match[1] == contractSchemaId && match[2] == contractTemplateName { - d.SetId(fmt.Sprintf("%s/templates/%s/anps/%s/epgs/%s/contracts/%s-%s-%s", schemaId, template, anp, epg, contractSchemaId, contractTemplateName, contract)) - d.Set("contract_name", contract) - d.Set("contract_schema_id", contractSchemaId) - d.Set("contract_template_name", contractTemplateName) - d.Set("relationship_type", models.StripQuotes(crefCont.S("relationshipType").String())) - found = true - break - } - } - - } - - } - } - } - } + relationshipType := d.Get("relationship_type").(string) + + index, _, err := getSchemaTemplateEPGContract(cont, template, anp, epg, contract, contractSchemaId, contractTemplateName, relationshipType) + if err != nil { + return err } - if !found { + if index == -1 { d.SetId("") return fmt.Errorf("Unable to find the ANP EPG Contract %s in Template %s of Schema Id %s ", contract, contractTemplateName, contractSchemaId) } + d.SetId(fmt.Sprintf("%s/templates/%s/anps/%s/epgs/%s/contracts/%s-%s-%s", schemaId, template, anp, epg, contractSchemaId, contractTemplateName, contract)) + d.Set("contract_name", contract) + d.Set("contract_schema_id", contractSchemaId) + d.Set("contract_template_name", contractTemplateName) + d.Set("relationship_type", relationshipType) + log.Printf("[DEBUG] %s: Read finished successfully", d.Id()) return nil diff --git a/mso/datasource_mso_schema_template_anp_epg_contract_test.go b/mso/datasource_mso_schema_template_anp_epg_contract_test.go new file mode 100644 index 00000000..d9de4afb --- /dev/null +++ b/mso/datasource_mso_schema_template_anp_epg_contract_test.go @@ -0,0 +1,64 @@ +package mso + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOSchemaTemplateAnpEpgContractDatasource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckMSOSchemaTemplateAnpEpgContractResourceDestroy, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: Read EPG Contract datasource not found error") }, + Config: testAccMSOSchemaTemplateAnpEpgContractDatasourceNotFoundConfig(), + ExpectError: regexp.MustCompile("Unable to find the ANP EPG Contract"), + }, + { + PreConfig: func() { fmt.Println("Test: Read EPG Contract datasource") }, + Config: testAccMSOSchemaTemplateAnpEpgContractDatasourceConfig(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.mso_schema_template_anp_epg_contract.contract", "schema_id"), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_contract.contract", "template_name", msoSchemaTemplateName), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_contract.contract", "anp_name", msoSchemaTemplateAnpName), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_contract.contract", "epg_name", msoSchemaTemplateAnpEpgName), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_contract.contract", "contract_name", msoSchemaTemplateContractName), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_contract.contract", "relationship_type", "provider"), + resource.TestCheckResourceAttrSet("data.mso_schema_template_anp_epg_contract.contract", "contract_schema_id"), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_contract.contract", "contract_template_name", msoSchemaTemplateName), + ), + }, + }, + }) +} + +func testAccMSOSchemaTemplateAnpEpgContractDatasourceConfig() string { + return fmt.Sprintf(`%s +data "mso_schema_template_anp_epg_contract" "contract" { + schema_id = mso_schema.%[2]s.id + template_name = "%[3]s" + anp_name = "%[4]s" + epg_name = "%[5]s" + contract_name = mso_schema_template_anp_epg_contract.%[6]s_provider.contract_name + relationship_type = "provider" +} +`, testAccMSOSchemaTemplateAnpEpgContractConfigProvider(), msoSchemaName, msoSchemaTemplateName, msoSchemaTemplateAnpName, msoSchemaTemplateAnpEpgName, msoSchemaTemplateContractName) +} + +func testAccMSOSchemaTemplateAnpEpgContractDatasourceNotFoundConfig() string { + return fmt.Sprintf(`%s +data "mso_schema_template_anp_epg_contract" "contract" { + schema_id = mso_schema.%[2]s.id + template_name = "%[3]s" + anp_name = "%[4]s" + epg_name = "%[5]s" + contract_name = "non_existent_contract" + relationship_type = "provider" +} +`, testAccMSOSchemaTemplateAnpEpgContractConfigProvider(), msoSchemaName, msoSchemaTemplateName, msoSchemaTemplateAnpName, msoSchemaTemplateAnpEpgName) +} diff --git a/mso/datasource_mso_schema_template_anp_epg_subnet_test.go b/mso/datasource_mso_schema_template_anp_epg_subnet_test.go new file mode 100644 index 00000000..fd11db49 --- /dev/null +++ b/mso/datasource_mso_schema_template_anp_epg_subnet_test.go @@ -0,0 +1,62 @@ +package mso + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOSchemaTemplateAnpEpgSubnetDatasource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckMSOSchemaTemplateAnpEpgSubnetDestroy, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: Read EPG Subnet datasource not found error") }, + Config: testAccMSOSchemaTemplateAnpEpgSubnetDatasourceNotFound(), + ExpectError: regexp.MustCompile("Unable to find the ANP EPG Subnet"), + }, + { + PreConfig: func() { fmt.Println("Test: Read EPG Subnet datasource") }, + Config: testAccMSOSchemaTemplateAnpEpgSubnetDatasource(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.mso_schema_template_anp_epg_subnet.subnet", "schema_id"), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_subnet.subnet", "template", msoSchemaTemplateName), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_subnet.subnet", "anp_name", msoSchemaTemplateAnpName), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_subnet.subnet", "epg_name", msoSchemaTemplateAnpEpgName), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_subnet.subnet", "ip", msoSchemaTemplateAnpEpgSubnetIp), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_subnet.subnet", "scope", "private"), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_subnet.subnet", "shared", "false"), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_subnet.subnet", "querier", "false"), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_subnet.subnet", "no_default_gateway", "false"), + resource.TestCheckResourceAttr("data.mso_schema_template_anp_epg_subnet.subnet", "primary", "false"), + ), + }, + }, + }) +} + +func testAccMSOSchemaTemplateAnpEpgSubnetDatasource() string { + return fmt.Sprintf(`%s + data "mso_schema_template_anp_epg_subnet" "subnet" { + schema_id = mso_schema.%[2]s.id + template = "%[3]s" + anp_name = "%[4]s" + epg_name = "%[5]s" + ip = mso_schema_template_anp_epg_subnet.%[6]s_subnet.ip + }`, testAccMSOSchemaTemplateAnpEpgSubnetConfigCreate(), msoSchemaName, msoSchemaTemplateName, msoSchemaTemplateAnpName, msoSchemaTemplateAnpEpgName, msoSchemaTemplateAnpEpgName) +} + +func testAccMSOSchemaTemplateAnpEpgSubnetDatasourceNotFound() string { + return fmt.Sprintf(`%s + data "mso_schema_template_anp_epg_subnet" "subnet" { + schema_id = mso_schema.%[2]s.id + template = "%[3]s" + anp_name = "%[4]s" + epg_name = "%[5]s" + ip = "99.99.99.99/32" + }`, testAccMSOSchemaTemplateAnpEpgSubnetConfigCreate(), msoSchemaName, msoSchemaTemplateName, msoSchemaTemplateAnpName, msoSchemaTemplateAnpEpgName) +} diff --git a/mso/resource_mso_schema_template_anp_epg_contract.go b/mso/resource_mso_schema_template_anp_epg_contract.go index 514ca384..7e13e6bb 100644 --- a/mso/resource_mso_schema_template_anp_epg_contract.go +++ b/mso/resource_mso_schema_template_anp_epg_contract.go @@ -62,11 +62,13 @@ func resourceMSOTemplateAnpEpgContract() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, + ForceNew: true, }, "contract_template_name": &schema.Schema{ Type: schema.TypeString, Optional: true, Computed: true, + ForceNew: true, }, "relationship_type": &schema.Schema{ Type: schema.TypeString, @@ -88,95 +90,41 @@ func resourceMSOTemplateAnpEpgContractImport(d *schema.ResourceData, m interface if err != nil { return nil, err } - d.Set("schema_id", schemaId) - count, err := cont.ArrayCount("templates") - if err != nil { - return nil, fmt.Errorf("No Template found") - } stateTemplate := get_attribute[2] - found := false stateANP := get_attribute[4] stateEPG := get_attribute[6] stateContract := get_attribute[8] stateRelationshipType := get_attribute[10] - for i := 0; i < count; i++ { - tempCont, err := cont.ArrayElement(i, "templates") - if err != nil { - return nil, err - } - apiTemplate := models.StripQuotes(tempCont.S("name").String()) - - if apiTemplate == stateTemplate { - d.Set("template_name", apiTemplate) - anpCount, err := tempCont.ArrayCount("anps") - if err != nil { - return nil, fmt.Errorf("Unable to get ANP list") - } - for j := 0; j < anpCount; j++ { - anpCont, err := tempCont.ArrayElement(j, "anps") - if err != nil { - return nil, err - } - apiANP := models.StripQuotes(anpCont.S("name").String()) - if apiANP == stateANP { - d.Set("anp_name", apiANP) - epgCount, err := anpCont.ArrayCount("epgs") - if err != nil { - return nil, fmt.Errorf("Unable to get EPG list") - } - for k := 0; k < epgCount; k++ { - epgCont, err := anpCont.ArrayElement(k, "epgs") - if err != nil { - return nil, err - } - apiEPG := models.StripQuotes(epgCont.S("name").String()) - if apiEPG == stateEPG { - d.Set("epg_name", apiEPG) - crefCount, err := epgCont.ArrayCount("contractRelationships") - if err != nil { - return nil, fmt.Errorf("Unable to get the contract relationships list") - } - for l := 0; l < crefCount; l++ { - crefCont, err := epgCont.ArrayElement(l, "contractRelationships") - if err != nil { - return nil, err - } - contractRef := models.StripQuotes(crefCont.S("contractRef").String()) - apiRelationshipType := models.StripQuotes(crefCont.S("relationshipType").String()) - re := regexp.MustCompile("/schemas/(.*)/templates/(.*)/contracts/(.*)") - match := re.FindStringSubmatch(contractRef) - apiContract := match[3] - if apiContract == stateContract && apiRelationshipType == stateRelationshipType { - d.SetId(apiContract) - d.Set("contract_name", match[3]) - d.Set("contract_schema_id", match[1]) - d.Set("contract_template_name", match[2]) - d.Set("relationship_type", apiRelationshipType) - found = true - break - } - } - - } - - } - } - } - } + index, crefCont, err := getSchemaTemplateEPGContract(cont, stateTemplate, stateANP, stateEPG, stateContract, schemaId, stateTemplate, stateRelationshipType) + if err != nil { + return nil, err } - - if !found { + if index == -1 { d.SetId("") return nil, fmt.Errorf("Unable to find the Contract %s", stateContract) } + contractRef := models.StripQuotes(crefCont.S("contractRef").String()) + re := regexp.MustCompile("/schemas/(.*)/templates/(.*)/contracts/(.*)") + match := re.FindStringSubmatch(contractRef) + + d.Set("schema_id", schemaId) + d.Set("template_name", stateTemplate) + d.Set("anp_name", stateANP) + d.Set("epg_name", stateEPG) + d.SetId(match[3]) + d.Set("contract_name", match[3]) + d.Set("contract_schema_id", match[1]) + d.Set("contract_template_name", match[2]) + d.Set("relationship_type", models.StripQuotes(crefCont.S("relationshipType").String())) + log.Printf("[DEBUG] %s: Import finished successfully", d.Id()) return []*schema.ResourceData{d}, nil } -func resourceMSOTemplateAnpEpgContractCreate(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] Template BD: Beginning Creation") +func resourceMSOTemplateAnpEpgContractUpdate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] Template ANP EPG Contract: Beginning Update") msoClient := m.(*client.Client) schemaID := d.Get("schema_id").(string) @@ -185,130 +133,51 @@ func resourceMSOTemplateAnpEpgContractCreate(d *schema.ResourceData, m interface epgName := d.Get("epg_name").(string) contractName := d.Get("contract_name").(string) - var relationship_type, contract_schemaid, contract_templatename string - if tempVar, ok := d.GetOk("relationship_type"); ok { - relationship_type = tempVar.(string) - } - - if tempVar, ok := d.GetOk("contract_schema_id"); ok { - contract_schemaid = tempVar.(string) - } else { - contract_schemaid = schemaID + contractSchemaId := d.Get("contract_schema_id").(string) + if contractSchemaId == "" { + contractSchemaId = schemaID } - if tempVar, ok := d.GetOk("contract_template_name"); ok { - contract_templatename = tempVar.(string) - } else { - contract_templatename = templateName + contractTemplateName := d.Get("contract_template_name").(string) + if contractTemplateName == "" { + contractTemplateName = templateName } - contractRefMap := make(map[string]interface{}) - contractRefMap["schemaId"] = contract_schemaid - contractRefMap["templateName"] = contract_templatename - contractRefMap["contractName"] = contractName - - path := fmt.Sprintf("/templates/%s/anps/%s/epgs/%s/contractRelationships/-", templateName, anpName, epgName) - bdStruct := models.NewTemplateAnpEpgContract("add", path, contractRefMap, relationship_type) + // Use the old relationship_type value to find the existing contract + oldRelationshipType, newRelationshipType := d.GetChange("relationship_type") - _, err := msoClient.PatchbyID(fmt.Sprintf("api/v1/schemas/%s", schemaID), bdStruct) + cont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/schemas/%s", schemaID)) if err != nil { return err } - return resourceMSOTemplateAnpEpgContractRead(d, m) -} - -func resourceMSOTemplateAnpEpgContractRead(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s: Beginning Read", d.Id()) - - msoClient := m.(*client.Client) - schemaId := d.Get("schema_id").(string) - - cont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/schemas/%s", schemaId)) + index, _, err := getSchemaTemplateEPGContract(cont, templateName, anpName, epgName, contractName, contractSchemaId, contractTemplateName, oldRelationshipType.(string)) if err != nil { - return errorForObjectNotFound(err, d.Id(), cont, d) + return err } - count, err := cont.ArrayCount("templates") - if err != nil { - return fmt.Errorf("No Template found") + if index == -1 { + return fmt.Errorf("Unable to find the Contract %s with relationship type %s", contractName, oldRelationshipType.(string)) } - stateTemplate := d.Get("template_name").(string) - found := false - stateANP := d.Get("anp_name") - stateEPG := d.Get("epg_name") - stateContract := d.Get("contract_name") - stateRelationshipType := d.Get("relationship_type") - for i := 0; i < count; i++ { - tempCont, err := cont.ArrayElement(i, "templates") + + updatePath := fmt.Sprintf("/templates/%s/anps/%s/epgs/%s/contractRelationships/%d", templateName, anpName, epgName, index) + payloadCon := container.New() + payloadCon.Array() + + if d.HasChange("relationship_type") { + err = addPatchPayloadToContainer(payloadCon, "replace", fmt.Sprintf("%s/relationshipType", updatePath), newRelationshipType.(string)) if err != nil { return err } - apiTemplate := models.StripQuotes(tempCont.S("name").String()) - - if apiTemplate == stateTemplate { - anpCount, err := tempCont.ArrayCount("anps") - if err != nil { - return fmt.Errorf("Unable to get ANP list") - } - for j := 0; j < anpCount; j++ { - anpCont, err := tempCont.ArrayElement(j, "anps") - if err != nil { - return err - } - apiANP := models.StripQuotes(anpCont.S("name").String()) - if apiANP == stateANP { - epgCount, err := anpCont.ArrayCount("epgs") - if err != nil { - return fmt.Errorf("Unable to get EPG list") - } - for k := 0; k < epgCount; k++ { - epgCont, err := anpCont.ArrayElement(k, "epgs") - if err != nil { - return err - } - apiEPG := models.StripQuotes(epgCont.S("name").String()) - if apiEPG == stateEPG { - crefCount, err := epgCont.ArrayCount("contractRelationships") - if err != nil { - return fmt.Errorf("Unable to get the contract relationships list") - } - for l := 0; l < crefCount; l++ { - crefCont, err := epgCont.ArrayElement(l, "contractRelationships") - if err != nil { - return err - } - contractRef := models.StripQuotes(crefCont.S("contractRef").String()) - apiRelationshipType := models.StripQuotes(crefCont.S("relationshipType").String()) - re := regexp.MustCompile("/schemas/(.*)/templates/(.*)/contracts/(.*)") - match := re.FindStringSubmatch(contractRef) - apiContract := match[3] - if apiContract == stateContract && apiRelationshipType == stateRelationshipType { - d.SetId(apiContract) - d.Set("contract_name", match[3]) - d.Set("contract_schema_id", match[1]) - d.Set("contract_template_name", match[2]) - d.Set("relationship_type", apiRelationshipType) - found = true - break - } - } - } - } - } - } - } } - if !found { - d.SetId("") + err = doPatchRequest(msoClient, fmt.Sprintf("api/v1/schemas/%s", schemaID), payloadCon) + if err != nil { + return err } - - log.Printf("[DEBUG] %s: Read finished successfully", d.Id()) - return nil - + return resourceMSOTemplateAnpEpgContractRead(d, m) } -func resourceMSOTemplateAnpEpgContractUpdate(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] Template BD: Beginning Update") +func resourceMSOTemplateAnpEpgContractCreate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] Template BD: Beginning Creation") msoClient := m.(*client.Client) schemaID := d.Get("schema_id").(string) @@ -338,28 +207,62 @@ func resourceMSOTemplateAnpEpgContractUpdate(d *schema.ResourceData, m interface contractRefMap["templateName"] = contract_templatename contractRefMap["contractName"] = contractName - id := d.Id() - cont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/schemas/%s", schemaID)) + path := fmt.Sprintf("/templates/%s/anps/%s/epgs/%s/contractRelationships/-", templateName, anpName, epgName) + bdStruct := models.NewTemplateAnpEpgContract("add", path, contractRefMap, relationship_type) + + _, err := msoClient.PatchbyID(fmt.Sprintf("api/v1/schemas/%s", schemaID), bdStruct) if err != nil { return err } - index, err := fetchindex(cont, templateName, anpName, epgName, id, relationship_type) + return resourceMSOTemplateAnpEpgContractRead(d, m) +} + +func resourceMSOTemplateAnpEpgContractRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] %s: Beginning Read", d.Id()) + + msoClient := m.(*client.Client) + + schemaId := d.Get("schema_id").(string) + + cont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/schemas/%s", schemaId)) + if err != nil { + return errorForObjectNotFound(err, d.Id(), cont, d) + } + templateName := d.Get("template_name").(string) + anpName := d.Get("anp_name").(string) + epgName := d.Get("epg_name").(string) + contractName := d.Get("contract_name").(string) + contractSchemaId := d.Get("contract_schema_id").(string) + if contractSchemaId == "" { + contractSchemaId = schemaId + } + contractTemplateName := d.Get("contract_template_name").(string) + if contractTemplateName == "" { + contractTemplateName = templateName + } + relationshipType := d.Get("relationship_type").(string) + + index, crefCont, err := getSchemaTemplateEPGContract(cont, templateName, anpName, epgName, contractName, contractSchemaId, contractTemplateName, relationshipType) if err != nil { return err } + if index == -1 { - fmt.Errorf("The given contract id is not found") + d.SetId("") + } else { + contractRef := models.StripQuotes(crefCont.S("contractRef").String()) + re := regexp.MustCompile("/schemas/(.*)/templates/(.*)/contracts/(.*)") + match := re.FindStringSubmatch(contractRef) + d.SetId(match[3]) + d.Set("contract_name", match[3]) + d.Set("contract_schema_id", match[1]) + d.Set("contract_template_name", match[2]) + d.Set("relationship_type", models.StripQuotes(crefCont.S("relationshipType").String())) } - indexs := strconv.Itoa(index) - path := fmt.Sprintf("/templates/%s/anps/%s/epgs/%s/contractRelationships/%s", templateName, anpName, epgName, indexs) - crefStruct := models.NewTemplateAnpEpgContract("replace", path, contractRefMap, relationship_type) + log.Printf("[DEBUG] %s: Read finished successfully", d.Id()) + return nil - _, errs := msoClient.PatchbyID(fmt.Sprintf("api/v1/schemas/%s", schemaID), crefStruct) - if errs != nil { - return errs - } - return resourceMSOTemplateAnpEpgContractRead(d, m) } func resourceMSOTemplateAnpEpgContractDelete(d *schema.ResourceData, m interface{}) error { @@ -393,12 +296,11 @@ func resourceMSOTemplateAnpEpgContractDelete(d *schema.ResourceData, m interface contractRefMap["templateName"] = contract_templatename contractRefMap["contractName"] = contractName - id := d.Id() cont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/schemas/%s", schemaID)) if err != nil { return err } - index, err := fetchindex(cont, templateName, anpName, epgName, id, relationship_type) + index, _, err := getSchemaTemplateEPGContract(cont, templateName, anpName, epgName, contractName, contract_schemaid, contract_templatename, relationship_type) if err != nil { return err } @@ -421,89 +323,75 @@ func resourceMSOTemplateAnpEpgContractDelete(d *schema.ResourceData, m interface return resourceMSOTemplateAnpEpgContractRead(d, m) } -func fetchindex(cont *container.Container, templateName, anpName, epgName, contractName, relationship_type string) (int, error) { +func getSchemaTemplateEPGContract(cont *container.Container, templateName, anpName, epgName, contractName, contractSchemaId, contractTemplateName, relationshipType string) (int, *container.Container, error) { found := false index := -1 count, err := cont.ArrayCount("templates") if err != nil { - return index, fmt.Errorf("No Template found") + return index, nil, fmt.Errorf("No Template found") } - for i := 0; i < count; i++ { - + for i := 0; i < count && !found; i++ { tempCont, err := cont.ArrayElement(i, "templates") if err != nil { - return index, err + return index, nil, err } currentTemplateName := models.StripQuotes(tempCont.S("name").String()) - if currentTemplateName == templateName { anpCount, err := tempCont.ArrayCount("anps") - if err != nil { - return index, fmt.Errorf("No Anp found") + return index, nil, fmt.Errorf("No Anp found") } - for j := 0; j < anpCount; j++ { + for j := 0; j < anpCount && !found; j++ { anpCont, err := tempCont.ArrayElement(j, "anps") - if err != nil { - return index, err + return index, nil, err } currentAnpName := models.StripQuotes(anpCont.S("name").String()) - log.Println("currentanpname", currentAnpName) if currentAnpName == anpName { - log.Println("found correct anpname") - epgCount, err := anpCont.ArrayCount("epgs") if err != nil { - return index, fmt.Errorf("No Epg found") + return index, nil, fmt.Errorf("No Epg found") } - for k := 0; k < epgCount; k++ { + for k := 0; k < epgCount && !found; k++ { epgCont, err := anpCont.ArrayElement(k, "epgs") if err != nil { - return index, err + return index, nil, err } currentEpgName := models.StripQuotes(epgCont.S("name").String()) - log.Println("currentepgname", currentEpgName) if currentEpgName == epgName { - log.Println("found correct epgname") - contractCount, err := epgCont.ArrayCount("contractRelationships") if err != nil { - return index, fmt.Errorf("No contractRelationships found") + return index, nil, fmt.Errorf("No contractRelationships found") } for s := 0; s < contractCount; s++ { contractCont, err := epgCont.ArrayElement(s, "contractRelationships") if err != nil { - return index, err + return index, nil, err } contractRef := models.StripQuotes(contractCont.S("contractRef").String()) apiRelationshipType := models.StripQuotes(contractCont.S("relationshipType").String()) re := regexp.MustCompile("/schemas/(.*)/templates/(.*)/contracts/(.*)") match := re.FindStringSubmatch(contractRef) + if match == nil { + return index, nil, fmt.Errorf("unexpected contractRef format: %s", contractRef) + } + apiContractSchemaId := match[1] + apiContractTemplateName := match[2] apiContract := match[3] - if apiContract == contractName && apiRelationshipType == relationship_type { - log.Println("found correct name") + if apiContract == contractName && + apiRelationshipType == relationshipType && + apiContractSchemaId == contractSchemaId && + apiContractTemplateName == contractTemplateName { index = s found = true - break + return index, contractCont, nil } } } - if found { - break - } } } - if found { - break - } } - - } - if found { - break } } - return index, nil - + return index, nil, nil } diff --git a/mso/resource_mso_schema_template_anp_epg_contract_test.go b/mso/resource_mso_schema_template_anp_epg_contract_test.go index 86a74126..fe7e4c09 100644 --- a/mso/resource_mso_schema_template_anp_epg_contract_test.go +++ b/mso/resource_mso_schema_template_anp_epg_contract_test.go @@ -11,217 +11,187 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/terraform" ) -func TestAccMSOSchemaTemplateAnpEpgContract_Basic(t *testing.T) { - var ss TemplateAnpEpgContract +// msoSchemaTemplateAnpEpgContractSchemaId is set during the first test step's Check to capture the dynamic schema ID for use in the manual deletion PreConfig step. +var msoSchemaTemplateAnpEpgContractSchemaId string + +func TestAccMSOSchemaTemplateAnpEpgContractResource(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckMSOSchemaTemplateAnpEpgContractDestroy, + CheckDestroy: testAccCheckMSOSchemaTemplateAnpEpgContractResourceDestroy, Steps: []resource.TestStep{ { - Config: testAccCheckMSOTemplateAnpEpgContractConfig_basic("provider"), - Check: resource.ComposeTestCheckFunc( - testAccCheckMSOSchemaTemplateAnpEpgContractExists("mso_schema_template_anp_epg_contract.contract", &ss), - testAccCheckMSOSchemaTemplateAnpEpgContractAttributes("provider", &ss), + PreConfig: func() { fmt.Println("Test: Create EPG Contract as provider") }, + Config: testAccMSOSchemaTemplateAnpEpgContractConfigProvider(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "schema_id"), + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "template_name", msoSchemaTemplateName), + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "anp_name", msoSchemaTemplateAnpName), + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "epg_name", msoSchemaTemplateAnpEpgName), + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "contract_name", msoSchemaTemplateContractName), + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "relationship_type", "provider"), + resource.TestCheckResourceAttrSet("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "contract_schema_id"), + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "contract_template_name", msoSchemaTemplateName), + // Capture the dynamic schema ID from state for use in the manual deletion PreConfig step. + func(s *terraform.State) error { + rs, ok := s.RootModule().Resources["mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider"] + if !ok { + return fmt.Errorf("EPG Contract resource not found in state") + } + msoSchemaTemplateAnpEpgContractSchemaId = rs.Primary.Attributes["schema_id"] + return nil + }, ), }, - }, - }) -} - -func TestAccMSOSchemaTemplateAnpEpgContract_Update(t *testing.T) { - var ss TemplateAnpEpgContract - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckMSOSchemaTemplateAnpEpgContractDestroy, - Steps: []resource.TestStep{ { - Config: testAccCheckMSOTemplateAnpEpgContractConfig_basic("provider"), - Check: resource.ComposeTestCheckFunc( - testAccCheckMSOSchemaTemplateAnpEpgContractExists("mso_schema_template_anp_epg_contract.contract", &ss), - testAccCheckMSOSchemaTemplateAnpEpgContractAttributes("provider", &ss), + PreConfig: func() { fmt.Println("Test: Update EPG Contract relationship_type to consumer") }, + Config: testAccMSOSchemaTemplateAnpEpgContractConfigConsumer(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "contract_name", msoSchemaTemplateContractName), + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "relationship_type", "consumer"), ), }, { - Config: testAccCheckMSOTemplateAnpEpgContractConfig_basic("consumer"), - Check: resource.ComposeTestCheckFunc( - testAccCheckMSOSchemaTemplateAnpEpgContractExists("mso_schema_template_anp_epg_contract.contract", &ss), - testAccCheckMSOSchemaTemplateAnpEpgContractAttributes("consumer", &ss), + PreConfig: func() { fmt.Println("Test: Reset EPG Contract relationship_type to provider") }, + Config: testAccMSOSchemaTemplateAnpEpgContractConfigProvider(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "contract_name", msoSchemaTemplateContractName), + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "relationship_type", "provider"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Import EPG Contract") }, + ResourceName: "mso_schema_template_anp_epg_contract." + msoSchemaTemplateContractName + "_provider", + ImportState: true, + ImportStateIdFunc: func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources["mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider"] + if !ok { + return "", fmt.Errorf("EPG Contract resource not found in state") + } + return fmt.Sprintf("%s/templates/%s/anps/%s/epgs/%s/contracts/%s/relationship_type/%s", + rs.Primary.Attributes["schema_id"], + rs.Primary.Attributes["template_name"], + rs.Primary.Attributes["anp_name"], + rs.Primary.Attributes["epg_name"], + rs.Primary.Attributes["contract_name"], + rs.Primary.Attributes["relationship_type"], + ), nil + }, + ImportStateVerify: true, + }, + { + PreConfig: func() { + fmt.Println("Test: Recreate EPG Contract after manual deletion from NDO") + msoClient := testAccProvider.Meta().(*client.Client) + cont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/schemas/%s", msoSchemaTemplateAnpEpgContractSchemaId)) + if err != nil { + t.Fatalf("Failed to get schema: %v", err) + } + index, _, err := getSchemaTemplateEPGContract(cont, msoSchemaTemplateName, msoSchemaTemplateAnpName, msoSchemaTemplateAnpEpgName, msoSchemaTemplateContractName, msoSchemaTemplateAnpEpgContractSchemaId, msoSchemaTemplateName, "provider") + if err != nil { + t.Fatalf("Failed to fetch contract index: %v", err) + } + if index == -1 { + t.Fatalf("Contract not found for manual deletion") + } + contractRemovePatchPayload := models.GetRemovePatchPayload(fmt.Sprintf("/templates/%s/anps/%s/epgs/%s/contractRelationships/%d", msoSchemaTemplateName, msoSchemaTemplateAnpName, msoSchemaTemplateAnpEpgName, index)) + _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/schemas/%s", msoSchemaTemplateAnpEpgContractSchemaId), contractRemovePatchPayload) + if err != nil { + t.Fatalf("Failed to manually delete contract: %v", err) + } + }, + Config: testAccMSOSchemaTemplateAnpEpgContractConfigProvider(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "contract_name", msoSchemaTemplateContractName), + resource.TestCheckResourceAttr("mso_schema_template_anp_epg_contract."+msoSchemaTemplateContractName+"_provider", "relationship_type", "provider"), ), }, }, }) } -func testAccCheckMSOTemplateAnpEpgContractConfig_basic(relationshiptype string) string { - return fmt.Sprintf(` - resource "mso_schema_template_anp_epg_contract" "contract" { - schema_id = "5c6c16d7270000c710f8094d" - template_name = "Template1" - anp_name = "WoS-Cloud-Only-2" - epg_name = "DB" - contract_name = "Internet-access" - relationship_type = "%v" - +func testAccMSOSchemaTemplateAnpEpgContractPrerequisiteConfig() string { + return fmt.Sprintf(`%s%s%s%s%s%s`, testSiteConfigAnsibleTest(), testTenantConfig(), testSchemaConfig(), testSchemaTemplateAnpConfig(), testSchemaTemplateAnpEpgConfig(), testSchemaTemplateFilterEntryConfig()) + testSchemaTemplateContractConfig() } -`, relationshiptype) + +func testAccMSOSchemaTemplateAnpEpgContractConfigProvider() string { + return fmt.Sprintf(`%[1]s +resource "mso_schema_template_anp_epg_contract" "%[2]s_provider" { + schema_id = mso_schema.%[3]s.id + template_name = "%[4]s" + anp_name = "%[5]s" + epg_name = mso_schema_template_anp_epg.%[6]s.name + contract_name = mso_schema_template_contract.%[2]s.contract_name + relationship_type = "provider" +} +`, testAccMSOSchemaTemplateAnpEpgContractPrerequisiteConfig(), msoSchemaTemplateContractName, msoSchemaName, msoSchemaTemplateName, msoSchemaTemplateAnpName, msoSchemaTemplateAnpEpgName) } -func testAccCheckMSOSchemaTemplateAnpEpgContractExists(contractName string, ss *TemplateAnpEpgContract) resource.TestCheckFunc { - return func(s *terraform.State) error { - client := testAccProvider.Meta().(*client.Client) - rs1, err1 := s.RootModule().Resources[contractName] +func testAccMSOSchemaTemplateAnpEpgContractConfigConsumer() string { + return fmt.Sprintf(`%[1]s +resource "mso_schema_template_anp_epg_contract" "%[2]s_provider" { + schema_id = mso_schema.%[3]s.id + template_name = "%[4]s" + anp_name = "%[5]s" + epg_name = mso_schema_template_anp_epg.%[6]s.name + contract_name = mso_schema_template_contract.%[2]s.contract_name + relationship_type = "consumer" +} +`, testAccMSOSchemaTemplateAnpEpgContractPrerequisiteConfig(), msoSchemaTemplateContractName, msoSchemaName, msoSchemaTemplateName, msoSchemaTemplateAnpName, msoSchemaTemplateAnpEpgName) +} - if !err1 { - return fmt.Errorf("Contract %s not found", contractName) - } - if rs1.Primary.ID == "" { - return fmt.Errorf("No Contract id was set") - } +// testAccCheckMSOSchemaTemplateAnpEpgContractResourceDestroy verifies the contract relationship is removed after test. +func testAccCheckMSOSchemaTemplateAnpEpgContractResourceDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*client.Client) - cont, err := client.GetViaURL("api/v1/schemas/5c6c16d7270000c710f8094d") - if err != nil { - return err - } - count, err := cont.ArrayCount("templates") - if err != nil { - return fmt.Errorf("No Template found") - } - tp := TemplateAnpEpgContract{} - found := false - for i := 0; i < count; i++ { - tempCont, err := cont.ArrayElement(i, "templates") + for _, rs := range s.RootModule().Resources { + if rs.Type == "mso_schema_template_anp_epg_contract" { + schemaID := rs.Primary.Attributes["schema_id"] + cont, err := client.GetViaURL(fmt.Sprintf("api/v1/schemas/%s", schemaID)) + if err != nil { + return nil + } + count, err := cont.ArrayCount("templates") if err != nil { - return err + return fmt.Errorf("No Template found") } - apiTemplateName := models.StripQuotes(tempCont.S("name").String()) - if apiTemplateName == "Template1" { + for i := 0; i < count; i++ { + tempCont, err := cont.ArrayElement(i, "templates") + if err != nil { + return fmt.Errorf("No template exists") + } anpCount, err := tempCont.ArrayCount("anps") if err != nil { - return fmt.Errorf("Unable to get ANP list") + return fmt.Errorf("No Anp found") } for j := 0; j < anpCount; j++ { anpCont, err := tempCont.ArrayElement(j, "anps") if err != nil { return err } - apiANP := models.StripQuotes(anpCont.S("name").String()) - if apiANP == "WoS-Cloud-Only-2" { - epgCount, err := anpCont.ArrayCount("epgs") - if err != nil { - return fmt.Errorf("Unable to get EPG list") - } - for k := 0; k < epgCount; k++ { - epgCont, err := anpCont.ArrayElement(k, "epgs") - if err != nil { - return err - } - apiEPG := models.StripQuotes(epgCont.S("name").String()) - if apiEPG == "DB" { - crefCount, err := epgCont.ArrayCount("contractRelationships") - if err != nil { - return fmt.Errorf("Unable to get the contract relationships list") - } - for l := 0; l < crefCount; l++ { - crefCont, err := epgCont.ArrayElement(l, "contractRelationships") - if err != nil { - return err - } - contractRef := models.StripQuotes(crefCont.S("contractRef").String()) - re := regexp.MustCompile("/schemas/(.*)/templates/(.*)/contracts/(.*)") - match := re.FindStringSubmatch(contractRef) - apiContract := match[3] - if apiContract == "Internet-access" { - tp.relationship_type = models.StripQuotes(crefCont.S("relationshipType").String()) - tp.contract_name = apiContract - found = true - break - } - } - } - - } - } - } - } - } - - if !found { - return fmt.Errorf("Contract not found from API") - } - - tp1 := &tp - - *ss = *tp1 - return nil - } -} - -func testAccCheckMSOSchemaTemplateAnpEpgContractDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*client.Client) - - for _, rs := range s.RootModule().Resources { - - if rs.Type == "mso_schema_template_anp_epg_contract" { - cont, err := client.GetViaURL("api/v1/schemas/5ea809672c00003bc40a2799") - if err != nil { - return nil - } else { - count, err := cont.ArrayCount("templates") - if err != nil { - return fmt.Errorf("No Template found") - } - for i := 0; i < count; i++ { - tempCont, err := cont.ArrayElement(i, "templates") + epgCount, err := anpCont.ArrayCount("epgs") if err != nil { - return fmt.Errorf("No Template exists") + return fmt.Errorf("Unable to get EPG list") } - apiTemplateName := models.StripQuotes(tempCont.S("name").String()) - if apiTemplateName == "Template1" { - anpCount, err := tempCont.ArrayCount("anps") + for k := 0; k < epgCount; k++ { + epgCont, err := anpCont.ArrayElement(k, "epgs") + if err != nil { + return err + } + crefCount, err := epgCont.ArrayCount("contractRelationships") if err != nil { - return fmt.Errorf("Unable to get ANP list") + return fmt.Errorf("Unable to get contract relationships list") } - for j := 0; j < anpCount; j++ { - anpCont, err := tempCont.ArrayElement(j, "anps") + for l := 0; l < crefCount; l++ { + crefCont, err := epgCont.ArrayElement(l, "contractRelationships") if err != nil { return err } - apiANP := models.StripQuotes(anpCont.S("name").String()) - if apiANP == "WoS-Cloud-Only-2" { - epgCount, err := anpCont.ArrayCount("epgs") - if err != nil { - return fmt.Errorf("Unable to get EPG list") - } - for k := 0; k < epgCount; k++ { - epgCont, err := anpCont.ArrayElement(k, "epgs") - if err != nil { - return err - } - apiEPG := models.StripQuotes(epgCont.S("name").String()) - if apiEPG == "DB" { - crefCount, err := epgCont.ArrayCount("contractRelationships") - if err != nil { - return fmt.Errorf("Unable to get the contract relationships list") - } - for l := 0; l < crefCount; l++ { - crefCont, err := epgCont.ArrayElement(l, "contractRelationships") - if err != nil { - return err - } - contractRef := models.StripQuotes(crefCont.S("contractRef").String()) - re := regexp.MustCompile("/schemas/(.*)/templates/(.*)/contracts/(.*)") - match := re.FindStringSubmatch(contractRef) - apiContract := match[3] - if apiContract == "Internet-access" { - return fmt.Errorf("Contract still exists.") - } - } - } - } - + contractRef := models.StripQuotes(crefCont.S("contractRef").String()) + re := regexp.MustCompile("/schemas/(.*)/templates/(.*)/contracts/(.*)") + match := re.FindStringSubmatch(contractRef) + if len(match) > 3 && match[3] == rs.Primary.Attributes["contract_name"] { + return fmt.Errorf("Schema Template ANP EPG Contract still exists") } } } @@ -231,16 +201,3 @@ func testAccCheckMSOSchemaTemplateAnpEpgContractDestroy(s *terraform.State) erro } return nil } -func testAccCheckMSOSchemaTemplateAnpEpgContractAttributes(relationship_type string, ss *TemplateAnpEpgContract) resource.TestCheckFunc { - return func(s *terraform.State) error { - if relationship_type != ss.relationship_type { - return fmt.Errorf("Bad Contract Relationship Type %s", ss.relationship_type) - } - return nil - } -} - -type TemplateAnpEpgContract struct { - contract_name string - relationship_type string -} diff --git a/mso/resource_mso_schema_template_anp_epg_subnet.go b/mso/resource_mso_schema_template_anp_epg_subnet.go index 85b7d1c2..ae87d17c 100644 --- a/mso/resource_mso_schema_template_anp_epg_subnet.go +++ b/mso/resource_mso_schema_template_anp_epg_subnet.go @@ -69,22 +69,28 @@ func resourceMSOSchemaTemplateAnpEpgSubnet() *schema.Resource { Optional: true, Computed: true, }, + // When set to true the attribute will error on NDO 4.1 + // Error: "EPG: epg in Schema: schema, Template: template, 'Querier' is only supported for Bridge Domain subnets"{} + // This attribute is applicable to BD subnets only and investigation should be done regarding deprecation "querier": &schema.Schema{ Type: schema.TypeBool, Optional: true, Computed: true, }, "description": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringLenBetween(1, 1000), + Type: schema.TypeString, + Optional: true, + // Computed: true, + ValidateFunc: validation.StringLenBetween(0, 1000), }, "no_default_gateway": &schema.Schema{ Type: schema.TypeBool, Optional: true, Computed: true, }, + // When set to true the attribute will error on NDO 4.1 + // Error: "EPG: epg in Schema: schema, Template: template, EPG Subnet 10.0.0.1/24 cannot be marked as primary"{} + // This attribute is applicable to BD subnets only and investigation should be done regarding deprecation "primary": &schema.Schema{ Type: schema.TypeBool, Optional: true, @@ -293,88 +299,90 @@ func resourceMSOSchemaTemplateAnpEpgSubnetUpdate(d *schema.ResourceData, m inter log.Printf("[DEBUG] Schema Template Anp Epg Subnet: Beginning Updating") msoClient := m.(*client.Client) - ips := d.Id() - - var schemaId string - if schema_id, ok := d.GetOk("schema_id"); ok { - schemaId = schema_id.(string) - } - - var templateName string - if template, ok := d.GetOk("template"); ok { - templateName = template.(string) - } - - var anpName string - if name, ok := d.GetOk("anp_name"); ok { - anpName = name.(string) - } + schemaId := d.Get("schema_id").(string) + templateName := d.Get("template").(string) + anpName := d.Get("anp_name").(string) + epgName := d.Get("epg_name").(string) - var epgName string - if name, ok := d.GetOk("epg_name"); ok { - epgName = name.(string) + conts, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/schemas/%s", schemaId)) + if err != nil { + return err } - var ip string - if tempVar, ok := d.GetOk("ip"); ok { - ip = tempVar.(string) + index, err := fetchIndex(conts, templateName, anpName, epgName, d.Id()) + if err != nil { + return err } - var description string - if tempVar, ok := d.GetOk("description"); ok { - description = tempVar.(string) + // We are keeping this conditional as a defensive check since this should not be triggered + // This conditional is here in case a subnet is manually deleted between the read and update operation + if index == -1 { + return fmt.Errorf("The given subnet ip is not found") } - scope := "private" - if tempVar, ok := d.GetOk("scope"); ok { - scope = tempVar.(string) - } + updatePath := fmt.Sprintf("/templates/%s/anps/%s/epgs/%s/subnets/%d", templateName, anpName, epgName, index) + payloadCont := container.New() + payloadCont.Array() - shared := false - if tempVar, ok := d.GetOk("shared"); ok { - shared = tempVar.(bool) + if d.HasChange("ip") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/ip", updatePath), d.Get("ip").(string)) + if err != nil { + return err + } } - primary := false - if tempVar, ok := d.GetOk("primary"); ok { - primary = tempVar.(bool) + if d.HasChange("description") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/description", updatePath), d.Get("description").(string)) + if err != nil { + return err + } } - querier := false - if tempVar, ok := d.GetOk("querier"); ok { - querier = tempVar.(bool) + if d.HasChange("scope") { + scope := d.Get("scope").(string) + if scope == "" { + scope = "private" + } + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/scope", updatePath), scope) + if err != nil { + return err + } } - noDefaultGateway := false - if tempVar, ok := d.GetOk("no_default_gateway"); ok { - noDefaultGateway = tempVar.(bool) + if d.HasChange("shared") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/shared", updatePath), d.Get("shared").(bool)) + if err != nil { + return err + } } - conts, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/schemas/%s", schemaId)) - if err != nil { - return err + if d.HasChange("querier") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/querier", updatePath), d.Get("querier").(bool)) + if err != nil { + return err + } } - index, err := fetchIndex(conts, templateName, anpName, epgName, ips) - if err != nil { - return err + if d.HasChange("no_default_gateway") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/noDefaultGateway", updatePath), d.Get("no_default_gateway").(bool)) + if err != nil { + return err + } } - if index == -1 { - fmt.Errorf("The given subnet ip is not found") + if d.HasChange("primary") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/primary", updatePath), d.Get("primary").(bool)) + if err != nil { + return err + } } - indexs := strconv.Itoa(index) - - schemaTemplateAnpEpgSubnetApp := models.NewSchemaTemplateAnpEpgSubnet("replace", fmt.Sprintf("/templates/%s/anps/%s/epgs/%s/subnets/%s", templateName, anpName, epgName, indexs), ip, description, scope, shared, noDefaultGateway, querier, primary) - - _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/schemas/%s", schemaId), schemaTemplateAnpEpgSubnetApp) + err = doPatchRequest(msoClient, fmt.Sprintf("api/v1/schemas/%s", schemaId), payloadCont) if err != nil { - log.Println(err) return err } - d.SetId(fmt.Sprintf("%v", ip)) + d.SetId(fmt.Sprintf("%v", d.Get("ip").(string))) log.Printf("[DEBUG] %s: Updating finished successfully", d.Id()) return resourceMSOSchemaTemplateAnpEpgSubnetRead(d, m) diff --git a/mso/resource_mso_schema_template_anp_epg_subnet_test.go b/mso/resource_mso_schema_template_anp_epg_subnet_test.go index d4d9fd76..eb1f296f 100644 --- a/mso/resource_mso_schema_template_anp_epg_subnet_test.go +++ b/mso/resource_mso_schema_template_anp_epg_subnet_test.go @@ -2,7 +2,6 @@ package mso import ( "fmt" - "strconv" "testing" "github.com/ciscoecosystem/mso-go-client/client" @@ -11,176 +10,291 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/terraform" ) -func TestAccMSOSchemaTemplateAnpEpgSubnet_Basic(t *testing.T) { - var ss SubnetTest +// Note: The `querier` and `primary` attributes are not tested because they are BD (Bridge Domain) specific attributes. +// - querier: API returns "EPG: in Schema: , Template: