From 82823d102bbd9105390435b097024263f76b81c3 Mon Sep 17 00:00:00 2001 From: Lionel Hercot Date: Mon, 3 Apr 2023 21:02:30 -0700 Subject: [PATCH] [bugfix] Fix issue with Client End Points when Endpoint is associated with an ESG --- aci/data_source_aci_fvcep.go | 92 +++++++++++++++---- aci/resource_aci_fvesg.go | 45 ++++----- examples/aci_client_end_point/main.tf | 23 +++++ website/docs/d/client_end_point.html.markdown | 4 + 4 files changed, 124 insertions(+), 40 deletions(-) create mode 100644 examples/aci_client_end_point/main.tf diff --git a/aci/data_source_aci_fvcep.go b/aci/data_source_aci_fvcep.go index 7b7d1a5f3..82e2d2594 100644 --- a/aci/data_source_aci_fvcep.go +++ b/aci/data_source_aci_fvcep.go @@ -102,6 +102,12 @@ func dataSourceAciClientEndPoint() *schema.Resource { Computed: true, }, + "esg_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "l2out_name": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -122,6 +128,14 @@ func dataSourceAciClientEndPoint() *schema.Resource { Type: schema.TypeString, }, }, + + "base_epg": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, }, }, }, @@ -135,6 +149,10 @@ func extractInfo(con *container.Container) (obj map[string]interface{}, dn strin infoMap["name"] = models.G(con, "name") infoMap["mac"] = models.G(con, "mac") infoMap["ip"] = models.G(con, "ip") + if infoMap["ip"] == "{}" { + infoMap["ip"] = "" + } + infoMap["vlan"] = models.G(con, "encap") dnInfoList := strings.Split(dnString, "/") @@ -152,6 +170,26 @@ func extractInfo(con *container.Container) (obj map[string]interface{}, dn strin level3Info := regexp.MustCompile("-").Split(dnInfoList[3], 2) if level3Info[0] == "epg" { infoMap["epg_name"] = level3Info[1] + } else if level3Info[0] == "esg" { + infoMap["esg_name"] = level3Info[1] + baseEpgDnInfoList := strings.Split(models.G(con, "baseEpgDn"), "/") + baseEpgTenantInfo := regexp.MustCompile("-").Split(baseEpgDnInfoList[1], 2) + if baseEpgTenantInfo[0] == "tn" { + baseEpgTenantName := baseEpgTenantInfo[1] + baseEpgLevel2Info := regexp.MustCompile("-").Split(baseEpgDnInfoList[2], 2) + if baseEpgLevel2Info[0] == "ap" { + baseEpgApplicationProfileName := baseEpgLevel2Info[1] + baseEpgLevel3Info := regexp.MustCompile("-").Split(baseEpgDnInfoList[3], 2) + if baseEpgLevel3Info[0] == "epg" { + baseEpgEpgName := baseEpgLevel3Info[1] + infoMap["base_epg"] = map[string]interface{}{ + "tenant_name": baseEpgTenantName, + "application_profile_name": baseEpgApplicationProfileName, + "epg_name": baseEpgEpgName, + } + } + } + } } else { return nil, "" } @@ -205,7 +243,7 @@ func getRemoteClientEndPoint(client *client.Client, query string, allowEmptyResu if query == "" { duURL = fmt.Sprintf("%s/fvCEp.json", baseURL) } else { - duURL = fmt.Sprintf("%s/fvCEp.json?query-target-filter=or(%s)&rsp-subtree=children&rsp-subtree-class=fvIp", baseURL, query) + duURL = fmt.Sprintf("%s/fvCEp.json?query-target-filter=and(%s)&rsp-subtree=children&rsp-subtree-class=fvIp", baseURL, query) } fvCEpCont, err := client.GetViaURL(duURL) @@ -230,21 +268,24 @@ func getRemoteClientEndPoint(client *client.Client, query string, allowEmptyResu objMap, dn := extractInfo(clientEndPointCont.S("fvCEp", "attributes")) // Reading fvIp object properties - fvIpObjects, err := clientEndPointCont.S("fvCEp").Children() - if err != nil { - return nil, nil, err - } - ips := make([]string, 0, 1) - + fvIpObjects, err := clientEndPointCont.S("fvCEp", "children").Children() if err == nil { - for j := 0; j < len(fvIpObjects); j++ { - ips = append(ips, models.G(fvIpObjects[j].S("fvIp", "attributes"), "addr")) + ips := make([]string, 0, 1) + + if err == nil { + for j := 0; j < len(fvIpObjects); j++ { + ips = append(ips, models.G(fvIpObjects[j].S("fvIp", "attributes"), "addr")) + } } - } - objMap["ips"] = ips - if len(ips) > 0 { - objMap["ip"] = ips[0] + objMap["ips"] = ips + if len(ips) > 0 { + objMap["ip"] = ips[0] + } + } else { + if errors.Is(err, container.ErrNotObjOrArray) == false { + return nil, nil, err + } } if dn != "" { @@ -267,6 +308,8 @@ func getRemoteClientEndPoint(client *client.Client, query string, allowEmptyResu func dataSourceAciClientEndPointRead(d *schema.ResourceData, m interface{}) error { aciClient := m.(*client.Client) + allowEmptyResult := d.Get("allow_empty_result").(bool) + var queryString string if mac, ok := d.GetOk("mac"); ok { if queryString != "" { @@ -292,6 +335,13 @@ func dataSourceAciClientEndPointRead(d *schema.ResourceData, m interface{}) erro } else { queryString = tempQueryString } + } else { + d.SetId("") + if allowEmptyResult { + return nil + } else { + return errors.New("Error retrieving Object: Object may not exists") + } } d.Set("ip", ip) } @@ -312,15 +362,21 @@ func dataSourceAciClientEndPointRead(d *schema.ResourceData, m interface{}) erro } } - allowEmptyResult := d.Get("allow_empty_result") - - objects, dns, err := getRemoteClientEndPoint(aciClient, queryString, allowEmptyResult.(bool)) + objects, dns, err := getRemoteClientEndPoint(aciClient, queryString, allowEmptyResult) if err != nil { return err } - d.Set("fvcep_objects", objects) - d.SetId(strings.Join(dns, " ")) + if objects != nil { + d.Set("fvcep_objects", objects) + } + + if dns != nil { + d.SetId(strings.Join(dns, " ")) + } else { + d.SetId("") + } + return nil } diff --git a/aci/resource_aci_fvesg.go b/aci/resource_aci_fvesg.go index ad4e8ca60..641746917 100644 --- a/aci/resource_aci_fvesg.go +++ b/aci/resource_aci_fvesg.go @@ -140,28 +140,29 @@ func resourceAciEndpointSecurityGroup() *schema.Resource { Optional: true, Description: "Create relation to vzBrCP", Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{"match_t": { - Optional: true, - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{ - "All", - "AtleastOne", - "AtmostOne", - "None", - }, false), - }, "prio": { - Optional: true, - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{ - "level1", - "level2", - "level3", - "level4", - "level5", - "level6", - "unspecified", - }, false), - }, + Schema: map[string]*schema.Schema{ + "match_t": { + Optional: true, + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "All", + "AtleastOne", + "AtmostOne", + "None", + }, false), + }, "prio": { + Optional: true, + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "level1", + "level2", + "level3", + "level4", + "level5", + "level6", + "unspecified", + }, false), + }, "target_dn": { Required: true, Type: schema.TypeString, diff --git a/examples/aci_client_end_point/main.tf b/examples/aci_client_end_point/main.tf new file mode 100644 index 000000000..c09eb162c --- /dev/null +++ b/examples/aci_client_end_point/main.tf @@ -0,0 +1,23 @@ +terraform { + required_providers { + aci = { + source = "ciscodevnet/aci" + } + } +} + +provider "aci" { + username = "" + password = "" + url = "" + insecure = true +} + +data "aci_client_end_point" "this" { + ip = "10.1.1.33" + allow_empty_result = true +} + +output "mac_endpoints" { + value = data.aci_client_end_point.this.fvcep_objects +} \ No newline at end of file diff --git a/website/docs/d/client_end_point.html.markdown b/website/docs/d/client_end_point.html.markdown index 3d100d459..393c92bc0 100644 --- a/website/docs/d/client_end_point.html.markdown +++ b/website/docs/d/client_end_point.html.markdown @@ -54,6 +54,10 @@ data "aci_client_end_point" "check" { - `fvcep_objects.vrf_name` - Parent VRF name of the Client End Point object. - `fvcep_objects.application_profile_name` - Parent Application Profile name of the Client End Point object. - `fvcep_objects.epg_name` - Parent EPG name of the Client End Point object. +- `fvcep_objects.esg_name` - Parent ESG name of the Client End Point object. - `fvcep_objects.l2out_name` - Parent L2Out name of the Client End Point object. - `fvcep_objects.instance_profile_name` - Parent Instance Profile name of the Client End Point object. - `fvcep_objects.endpoint_path` - List of endpoint paths associated with the Client End Point object. +- `fvcep_objects.base_epg.tenant_name` - Name of the Tenant of the base EPG of the Client End Point object when the Client End Point is associated with an ESG. +- `fvcep_objects.base_epg.application_profile_name` - Name of the Application Profile of the base EPG of the Client End Point object when the Client End Point is associated with an ESG. +- `fvcep_objects.base_epg.epg_name` - Name of the base EPG of the Client End Point object when the Client End Point is associated with an ESG.