<filename>DataCenter (ACI)/ACIOperations/ACIOperations/ACIOps.py<gh_stars>1-10
import requests
import json
import warnings
import xml.etree.ElementTree as ET
import re
import collections
import ipaddress


class AciOps:

    """Collects authentication information from user, returns session if successfull, or response if not"""

    def __init__(self):

        self.session = None
        self.response = None
        self.apic = None
        self.vlan_dict = collections.defaultdict(list)
        self.policies_dict = collections.defaultdict(list)
        self.device_info = []
        self.tenant_array = []

    def login(self, **kwargs):

        """APIC authentication method. Takes username, password, apic kwargs and returns session"""

        ignore_warning = warnings.filterwarnings('ignore', message='Unverified HTTPS request')
        uri = "https://%s/api/aaaLogin.json" % kwargs["apic"]
        json_auth = {'aaaUser': {'attributes': {'name': kwargs["username"], 'pwd': kwargs["password"]}}}
        json_credentials = json.dumps(json_auth)
        self.session = requests.Session()
        self.apic = kwargs["apic"]

        try:
            request = self.session.post(uri, data=json_credentials, verify=False)
            self.response = json.loads(request.text)
        except (requests.exceptions.ConnectionError, requests.exceptions.InvalidURL):
            raise ConnectionError("Login Failed, Verify APIC Address")

        try:
            if self.response["imdata"][0]["error"]["attributes"]["code"] == "401":
                raise ValueError("Login Failed, please verify credentials | Credential Submitted:\n{}\n{}".format(
                                                                            kwargs["username"], kwargs["password"]))
        except TypeError:
            raise TypeError("Something Went Wrong")
        except KeyError:
            pass
        else:
            return self.session


    def vlan_pools(self):

        """Get fabric vlan pools, return pool in a dictionary data structure"""

        vlan_range_dict = collections.defaultdict(list)

        uri = "https://" + self.apic + "/api/node/mo/uni/infra.xml?query-target=subtree&target-subtree-class=fvnsVlanInstP&target-subtree-class=fvnsEncapBlk&query-target=subtree&rsp-subtree=full&rsp-subtree-class=tagAliasInst"
        request = self.session.get(uri, verify=False)
        root = ET.fromstring(request.text)

        for fvnsEncapBlk in root.iter("fvnsEncapBlk"):
            vlan_pool_array = []
            if "vxlan" in fvnsEncapBlk.get("from"):
                continue
            else:
                vlan_pool_array.append(fvnsEncapBlk.get("from"))
                vlan_pool_array.append(fvnsEncapBlk.get("to"))
                start_range = fvnsEncapBlk.get("from")
                end_range = fvnsEncapBlk.get("to")
                dn = fvnsEncapBlk.get("dn")
                parse_dn = re.findall(r'(?<=vlanns-\[).*?(?=]-[a-z])', dn)
                vlan_range_dict[parse_dn[0]] = start_range + "-" + end_range

                vlans = []
                for vlan in vlan_pool_array:
                    remove_vlan = vlan.replace("vlan-", "")
                    vlan_range = remove_vlan.split("-")
                    vlans.append(vlan_range[0])

                if "vxlan" in vlans:
                    pass
                else:
                    vlans_unpacked = []
                    vlan_start = int(vlans[0])
                    vlan_end = int(vlans[1]) + 1

                    if vlan_start == vlan_end:
                        vlans_unpacked.append(str(vlan_end))
                    else:
                        begin = vlan_start
                        for i in range(vlan_start, vlan_end):
                            vlans_unpacked.append(str(begin))
                            begin = begin + 1

                self.vlan_dict[parse_dn[0]].append(vlans_unpacked)

        return vlan_range_dict

    def find_encap(self, **kwargs):

        """Takes in the vlan encapsulation, intiaates vlan_pool(0 and policy_mappings. Calls _find_encap_comiple to fin fabric information about the encapsulation
        Returns a series of list to the caller"""

        vlan_pool = collections.defaultdict(list)
        phys_doms = collections.defaultdict(list)
        aaeps = collections.defaultdict(list)
        location = collections.defaultdict(list)
        path = collections.defaultdict(list)

        self.vlan_pools()
        self.policy_mappings()

        pools = self._find_encap_compile(vlan=kwargs["vlan"])
        vlan_pool[kwargs["vlan"]].append(pools[0])
        phys_doms[kwargs["vlan"]].append(pools[1])
        aaeps[kwargs["vlan"]].append(pools[2])
        location[kwargs["vlan"]].append(pools[3])
        path[kwargs["vlan"]].append(pools[4])

        unpacked_vlan_pools = [v for k, v in vlan_pool.items() for v in v for v in v]
        unpacked_phys_doms = [v for k, v in phys_doms.items() for v in v for v in v]
        unpacked_aaep = [v for k, v in aaeps.items() for v in v for v in v]
        unpacked_location = [v for k, v in location.items() for v in v for v in v]
        unpacked_path = [v for k, v in path.items() for v in v for v in v]

        return unpacked_vlan_pools, unpacked_phys_doms, unpacked_aaep, unpacked_location, unpacked_path


    def _find_encap_compile(self, **kwargs):

        """ This method is for local use only. It works with vlan_pool() to produce a series of list and return them to the call find_encap"""

        pools = []
        phy_doms = []
        aaep = []
        location = []
        path = []

        uri = "https://{}/api/class/fvRsPathAtt.xml?query-target-filter=eq(fvRsPathAtt.encap,\"vlan-{}\")".format(self.apic, kwargs["vlan"])
        request = self.session.get(uri, verify=False)
        root = ET.fromstring(request.text)

        if "\"0\"" in request.text:
            print("Encap not Found")
        else:
            for fvRsPathAtt in root.iter("fvRsPathAtt"):
                string = fvRsPathAtt.get("dn")
                tenant = re.findall(r'(?<=tn-).*(?=/ap)', string)
                location.append("Tenant: " + tenant[0])
                ap = re.findall(r'(?<=ap-).*(?=/ep)', string)
                location.append("App Profile: " + ap[0])
                epg = re.findall(r'(?<=epg-).*(?=/rsp)', string)
                location.append("EPG: " + epg[0])

                if re.findall(r'(?<=protpaths-).*(?=/pat)', string):
                    path_1 = re.findall(r'(?<=protpaths-).*(?=/pat)', string)
                elif re.findall(r'(?<=paths-).*(?=/pat)', string):
                    path_1 = re.findall(r'(?<=paths-).*(?=/pat)', string)

                profile = re.findall(r'(?<=pathep-\[).*(?=]])', string)
                path.append("Path: " + path_1[0] + ": " + profile[0])

            for key_1, value_1 in self.vlan_dict.items():
                for v in value_1:
                    for v in v:
                        if kwargs["vlan"] == v:
                            pools.append(key_1)
                            for key_2, value_2 in self.policies_dict.items():
                                if key_2 == key_1:
                                    for v in value_2:
                                        phy_doms.append(v)
                            for key_3, value_3 in self.policies_dict.items():
                                for dom in phy_doms:
                                    if dom in value_3:
                                        if "AAEP" in key_3:
                                            aaep.append(key_3)
                                        else:
                                            continue
                                    else:
                                        pass
                        else:
                            continue

            dup_pools = list(dict.fromkeys(pools))
            dup_location = list(dict.fromkeys(location))
            dup_aaep = list(dict.fromkeys(aaep))

            return dup_pools, phy_doms, dup_aaep, dup_location, path

    def policy_mappings(self):

        """Maps AAEPS, Vlan Pools, and phys/vmm/routed domain. Return dictionary data structure"""

        uri = "https://" + self.apic + "/api/node/mo/uni.xml?query-target=subtree&target-subtree-class=physDomP&target-subtree-class=infraRsVlanNs,infraRtDomP&query-target=subtree"
        headers = {'content-type': 'text/xml'}

        request = self.session.get(uri, verify=False, headers=headers)
        root = ET.fromstring(request.text)

        for infraRtDomP in root.iter("infraRtDomP"):
            string = infraRtDomP.get("dn")
            if re.findall(r'phys-.*?[/]\b', string):
                aaeps = re.findall(r'(?<=attentp-).*(?=])', string)
                phys_dom = re.findall(r'(?<=phys-).*(?=/rt)', string)
                self.policies_dict["AAEP " + aaeps[0]].append(phys_dom)
            elif re.findall(r'l3dom-.*?[/]\b', string):
                aaeps = re.findall(r'(?<=attentp-).*(?=])', string)
                l3_dom = re.findall(r'(?<=l3dom-).*(?=/rt)', string)
                self.policies_dict["AAEP " + aaeps[0]].append(l3_dom)
            elif re.findall(r'vmmp-.*?[/]\b', string):
                aaeps = re.findall(r'(?<=attentp-).*(?=])', string)
                vmm_dom = re.findall(r'(?<=vmmp-).*(?=/rt)', string)
                self.policies_dict["AAEP " + aaeps[0]].append(vmm_dom[0])
            else:
                continue

        for infraRsVlanNs in root.iter("infraRsVlanNs"):

            vl_pool_dn = infraRsVlanNs.get("tDn")
            phys_dom_dn = infraRsVlanNs.get("dn")

            if re.findall(r'(?<=phys-).*(?=/)', phys_dom_dn):
                phys_dom = re.findall(r'(?<=phys-).*(?=/)', phys_dom_dn)
                vlan_pool = re.findall(r'(?<=vlanns-\[).*(?=])', vl_pool_dn)
                self.policies_dict[vlan_pool[0]].append(phys_dom)
            elif re.findall(r'(?<=ledom-).*(?=/)', phys_dom_dn):
                l3_dom = re.findall(r'(?<=l3dom-).*(?=/)', phys_dom_dn)
                vlan_pool = re.findall(r'(?<=vlanns-\[).*(?=])', vl_pool_dn)
                self.policies_dict[vlan_pool[0]].append(l3_dom)
            elif re.findall(r'(?<=vmmp-).*(?=/)', phys_dom_dn):
                vmm_dom = re.findall(r'(?<=vmmp-).*(?=/)', phys_dom_dn)
                vlan_pool = re.findall(r'(?<=vlanns-\[).*(?=])', vl_pool_dn)
                self.policies_dict[vlan_pool[0]].append(vmm_dom[0])
            else:
                continue

        return self.policies_dict

    def infr(self, **kwargs):


        """Takes in pod number , and return all information about the fabric hardware. Greate for TAC use"""

        pod_num = kwargs["pod"]
        pod_number = "pod-{}".format(pod_num)

        uri = "https://{}/api/node/mo/topology/{}.xml?query-target=children".format(self.apic, pod_number)

        request = self.session.get(uri, verify=False)
        root = ET.fromstring(request.text)

        for fabricNode in root.iter("fabricNode"):
            fabric_node = fabricNode.get("name")
            model_node = fabricNode.get("model")
            serial_node = fabricNode.get("serial")
            self.device_info.append(fabric_node)
            self.device_info.append(model_node)
            self.device_info.append(serial_node)

        if not self.device_info:
            return "No Infrastructor Information"
        else:
            return self.device_info

    def view_tenants(self):

        """Returns ACI Tenant from the arbitrary APIC"""

        uri = "https://{}/api/class/fvTenant.json".format(self.apic)

        request = self.session.get(uri, verify=False)
        response_dict = request.json()
        total_count = int(response_dict["totalCount"])

        try:
            index = 0
            for i in range(0, total_count):
                self.tenant_array.append(response_dict["imdata"][index]["fvTenant"]["attributes"]["name"])
                index = index + 1
        except IndexError:
            pass

        return self.tenant_array

    def subnet_finder(self, **kwargs):

        """ Takes in kwarg subnet and finds all details about the subnet (BD, Tenant, scope etc."""


        self.view_tenants()

        uri = "https://{}/api/class/fvBD.xml?query-target=subtree".format(self.apic)
        request = self.session.get(uri, verify=False)
        root = ET.fromstring(request.text)

        for fvSubnet in root.iter("fvSubnet"):
            location = fvSubnet.get("dn")
            ip = fvSubnet.get("ip")
            if kwargs["subnet"] in ip:
                gps = location
                gps_ip = ip
                scope = fvSubnet.get("scope")

        try:
            for fvBD in root.iter("fvBD"):
                bridge_domain = fvBD.get("name")
                if re.findall('[?<=/BD-]' + bridge_domain + '(?=/)', gps):
                    gps_bd = bridge_domain
                    uni_route = fvBD.get("unicastRoute")
                    unkwn_uni = fvBD.get("unkMacUcastAct")

            for fvRsCtx in root.iter("fvRsCtx"):
                vrf = fvRsCtx.get("tnFvCtxName")
                location = fvRsCtx.get("dn")
                if re.findall('[?<=/BD-]' + gps_bd + '(?=/)', location):
                    gps_vrf = vrf

            aps = []
            epgs = []

            for fvRtBd in root.iter("fvRtBd"):
                dn = fvRtBd.get("dn")
                if re.findall('[?<=/BD-]' + gps_bd + '(?=/)', dn):
                    ap = re.findall(r'(?<=ap-).*(?=/ep)', dn)
                    aps.append(ap[0])
                    epg = re.findall(r'(?<=epg-).*(?=\])', dn)
                    epgs.append(epg[0])
                else:
                    pass

            for fvRsBDToOut in root.iter("fvRsBDToOut"):
                dn = fvRsBDToOut.get("dn")
                if re.findall('[?<=/BD-]' + gps_bd + '(?=/)', dn):
                    if not fvRsBDToOut.get("tnL3extOutName"):
                        l3out = "N/A"
                    else:
                        l3out = fvRsBDToOut.get("tnL3extOutName")

            for tenant in self.tenant_array:
                if tenant in gps:
                    gps_tenant = tenant
                else:
                    continue

            unpack_ap = [i for i in aps]
            unpack_epg = [i for i in epgs]

        except UnboundLocalError:
            pass

        return gps_ip, gps_tenant, gps_bd, vrf, l3out, uni_route, scope, unkwn_uni, unpack_ap, unpack_epg

    def tenant_vrf(self, **kwargs):

        """View Tenant vrf, return Tenant vrf names"""

        vrf_dict = collections.defaultdict(list)
        uri = "https://{}/api/node/mo/uni/tn-{}.json?query-target=children&target-subtree-class=fvCtx"\
                                                                    .format(self.apic, kwargs["tenant"])
        request = self.session.get(uri, verify=False)
        response = json.loads(request.text)

        try:
            index = 0
            for i in range(0, 100):
                vrf_dict["vrf"].append(response["imdata"][index]["fvCtx"]["attributes"]["name"])
                index = index + 1
        except IndexError:
            pass

        return vrf_dict

    def view_bd(self, **kwargs):

        """View Bridge domains of a Tenant, returns bridge domain names"""

        bd_dict = collections.defaultdict(list)
        uri = "https://{}/api/node/mo/uni/tn-{}.json?query-target=children&target-subtree-class=fvBD"\
                                                                    .format(self.apic, kwargs["tenant"])
        request = self.session.get(uri, verify=False)
        response = json.loads(request.text)
        total_count = int(response["totalCount"])

        index = 0
        for i in range(0, total_count):
            bd_dict["name"].append(response["imdata"][index]["fvBD"]["attributes"]["name"])
            index = index + 1

        return bd_dict

    def view_app_profiles(self, **kwargs):

        """View Application profiles of a particular Tenant, return App profiles"""

        app_dict = collections.defaultdict(list)
        uri = "https://{}/api/node/mo/uni/tn-{}.json?query-target=children&target-subtree-class=fvAp"\
                                                                    .format(self.apic, kwargs["tenant"])
        request = self.session.get(uri, verify=False)
        response = json.loads(request.text)
        total_count = int(response["totalCount"])

        index = 0
        for i in range(0, total_count):
            app_dict["name"].append(response["imdata"][index]["fvAp"]["attributes"]["name"])
            index = index + 1

        return app_dict

    def view_epgs(self, **kwargs):

        """View endpoint groups of a particular Tenant-App profile, returns EPG names"""

        epg_dict = collections.defaultdict(list)
        uri = "https://{}/api/node/mo/uni/tn-{}/ap-{}.json?query-target=children&target-subtree-class=fvAEPg"\
                                                            .format(self.apic, kwargs["tenant"], kwargs["app"])
        request = self.session.get(uri, verify=False)
        response = json.loads(request.text)
        total_count = int(response["totalCount"])

        index = 0
        for i in range(0, total_count):
            epg_dict["name"].append(response["imdata"][index]["fvAEPg"]["attributes"]["name"])
            index = index + 1

        return epg_dict

    def enpoint_tracker(self, **kwargs):

        """This method take in a IP or MAC address and returns the endpoint data. Return string if no endpoint is found"""

        try:
            ipaddress.IPv4Address(kwargs["endpoint"])
            uri = "https://%s" % self.apic + "/api/node/class/fvCEp.xml?rsp-subtree=full&rsp-subtree-include=" \
                                     "required&rsp-subtree-filter=eq(fvIp.addr," + "\"%s\"" % kwargs["endpoint"]

        except ValueError:
            uri = "https://%s" % self.apic + "/api/node/class/fvCEp.xml?rsp-subtree=full&rsp-subtree-class=" \
                  "fvCEp,fvRsCEpToPathEp,fvIp,fvRsHyper,fvRsToNic,fvRsToVm&query-target-filter=eq(fvCEp.mac," \
                  + "\"%s\"" % kwargs["endpoint"]

        request = self.session.get(uri, verify=False)
        root = ET.fromstring(request.text)

        for fvCEp in root.iter("fvCEp"):
            print(fvCEp)
            ep_name = fvCEp.get("name")
            ep_mac = fvCEp.get("mac")
            encap = fvCEp.get("encap")
            ep_loc = fvCEp.get("dn")
            ep_ip = fvCEp.get("ip")

            endpoint = ("Name: {0:20}\nEP: {1:<20}\nEncapsulation: {2:<20}\nLocation: {3:<20}\nIP: {4:<20}"
                                                                .format(ep_name,ep_mac,encap,ep_loc,ep_ip))


        try:
            return endpoint
        except UnboundLocalError:
            return  "Endpoint Not Found"


<filename>DataCenter (ACI)/ACIOperations/ACIOperations/ACIOps.py<gh_stars>1-10
import requests
import json
import warnings
import xml.etree.ElementTree as ET
import re
import collections
import ipaddress


class AciOps:

    """Collects authentication information from user, returns session if successfull, or response if not"""

    def __init__(self):

        self.session = None
        self.response = None
        self.apic = None
        self.vlan_dict = collections.defaultdict(list)
        self.policies_dict = collections.defaultdict(list)
        self.device_info = []
        self.tenant_array = []

    def login(self, **kwargs):

        """APIC authentication method. Takes username, password, apic kwargs and returns session"""

        ignore_warning = warnings.filterwarnings('ignore', message='Unverified HTTPS request')
        uri = "https://%s/api/aaaLogin.json" % kwargs["apic"]
        json_auth = {'aaaUser': {'attributes': {'name': kwargs["username"], 'pwd': kwargs["password"]}}}
        json_credentials = json.dumps(json_auth)
        self.session = requests.Session()
        self.apic = kwargs["apic"]

        try:
            request = self.session.post(uri, data=json_credentials, verify=False)
            self.response = json.loads(request.text)
        except (requests.exceptions.ConnectionError, requests.exceptions.InvalidURL):
            raise ConnectionError("Login Failed, Verify APIC Address")

        try:
            if self.response["imdata"][0]["error"]["attributes"]["code"] == "401":
                raise ValueError("Login Failed, please verify credentials | Credential Submitted:\n{}\n{}".format(
                                                                            kwargs["username"], kwargs["password"]))
        except TypeError:
            raise TypeError("Something Went Wrong")
        except KeyError:
            pass
        else:
            return self.session


    def vlan_pools(self):

        """Get fabric vlan pools, return pool in a dictionary data structure"""

        vlan_range_dict = collections.defaultdict(list)

        uri = "https://" + self.apic + "/api/node/mo/uni/infra.xml?query-target=subtree&target-subtree-class=fvnsVlanInstP&target-subtree-class=fvnsEncapBlk&query-target=subtree&rsp-subtree=full&rsp-subtree-class=tagAliasInst"
        request = self.session.get(uri, verify=False)
        root = ET.fromstring(request.text)

        for fvnsEncapBlk in root.iter("fvnsEncapBlk"):
            vlan_pool_array = []
            if "vxlan" in fvnsEncapBlk.get("from"):
                continue
            else:
                vlan_pool_array.append(fvnsEncapBlk.get("from"))
                vlan_pool_array.append(fvnsEncapBlk.get("to"))
                start_range = fvnsEncapBlk.get("from")
                end_range = fvnsEncapBlk.get("to")
                dn = fvnsEncapBlk.get("dn")
                parse_dn = re.findall(r'(?<=vlanns-\[).*?(?=]-[a-z])', dn)
                vlan_range_dict[parse_dn[0]] = start_range + "-" + end_range

                vlans = []
                for vlan in vlan_pool_array:
                    remove_vlan = vlan.replace("vlan-", "")
                    vlan_range = remove_vlan.split("-")
                    vlans.append(vlan_range[0])

                if "vxlan" in vlans:
                    pass
                else:
                    vlans_unpacked = []
                    vlan_start = int(vlans[0])
                    vlan_end = int(vlans[1]) + 1

                    if vlan_start == vlan_end:
                        vlans_unpacked.append(str(vlan_end))
                    else:
                        begin = vlan_start
                        for i in range(vlan_start, vlan_end):
                            vlans_unpacked.append(str(begin))
                            begin = begin + 1

                self.vlan_dict[parse_dn[0]].append(vlans_unpacked)

        return vlan_range_dict

    def find_encap(self, **kwargs):

        """Takes in the vlan encapsulation, intiaates vlan_pool(0 and policy_mappings. Calls _find_encap_comiple to fin fabric information about the encapsulation
        Returns a series of list to the caller"""

        vlan_pool = collections.defaultdict(list)
        phys_doms = collections.defaultdict(list)
        aaeps = collections.defaultdict(list)
        location = collections.defaultdict(list)
        path = collections.defaultdict(list)

        self.vlan_pools()
        self.policy_mappings()

        pools = self._find_encap_compile(vlan=kwargs["vlan"])
        vlan_pool[kwargs["vlan"]].append(pools[0])
        phys_doms[kwargs["vlan"]].append(pools[1])
        aaeps[kwargs["vlan"]].append(pools[2])
        location[kwargs["vlan"]].append(pools[3])
        path[kwargs["vlan"]].append(pools[4])

        unpacked_vlan_pools = [v for k, v in vlan_pool.items() for v in v for v in v]
        unpacked_phys_doms = [v for k, v in phys_doms.items() for v in v for v in v]
        unpacked_aaep = [v for k, v in aaeps.items() for v in v for v in v]
        unpacked_location = [v for k, v in location.items() for v in v for v in v]
        unpacked_path = [v for k, v in path.items() for v in v for v in v]

        return unpacked_vlan_pools, unpacked_phys_doms, unpacked_aaep, unpacked_location, unpacked_path


    def _find_encap_compile(self, **kwargs):

        """ This method is for local use only. It works with vlan_pool() to produce a series of list and return them to the call find_encap"""

        pools = []
        phy_doms = []
        aaep = []
        location = []
        path = []

        uri = "https://{}/api/class/fvRsPathAtt.xml?query-target-filter=eq(fvRsPathAtt.encap,\"vlan-{}\")".format(self.apic, kwargs["vlan"])
        request = self.session.get(uri, verify=False)
        root = ET.fromstring(request.text)

        if "\"0\"" in request.text:
            print("Encap not Found")
        else:
            for fvRsPathAtt in root.iter("fvRsPathAtt"):
                string = fvRsPathAtt.get("dn")
                tenant = re.findall(r'(?<=tn-).*(?=/ap)', string)
                location.append("Tenant: " + tenant[0])
                ap = re.findall(r'(?<=ap-).*(?=/ep)', string)
                location.append("App Profile: " + ap[0])
                epg = re.findall(r'(?<=epg-).*(?=/rsp)', string)
                location.append("EPG: " + epg[0])

                if re.findall(r'(?<=protpaths-).*(?=/pat)', string):
                    path_1 = re.findall(r'(?<=protpaths-).*(?=/pat)', string)
                elif re.findall(r'(?<=paths-).*(?=/pat)', string):
                    path_1 = re.findall(r'(?<=paths-).*(?=/pat)', string)

                profile = re.findall(r'(?<=pathep-\[).*(?=]])', string)
                path.append("Path: " + path_1[0] + ": " + profile[0])

            for key_1, value_1 in self.vlan_dict.items():
                for v in value_1:
                    for v in v:
                        if kwargs["vlan"] == v:
                            pools.append(key_1)
                            for key_2, value_2 in self.policies_dict.items():
                                if key_2 == key_1:
                                    for v in value_2:
                                        phy_doms.append(v)
                            for key_3, value_3 in self.policies_dict.items():
                                for dom in phy_doms:
                                    if dom in value_3:
                                        if "AAEP" in key_3:
                                            aaep.append(key_3)
                                        else:
                                            continue
                                    else:
                                        pass
                        else:
                            continue

            dup_pools = list(dict.fromkeys(pools))
            dup_location = list(dict.fromkeys(location))
            dup_aaep = list(dict.fromkeys(aaep))

            return dup_pools, phy_doms, dup_aaep, dup_location, path

    def policy_mappings(self):

        """Maps AAEPS, Vlan Pools, and phys/vmm/routed domain. Return dictionary data structure"""

        uri = "https://" + self.apic + "/api/node/mo/uni.xml?query-target=subtree&target-subtree-class=physDomP&target-subtree-class=infraRsVlanNs,infraRtDomP&query-target=subtree"
        headers = {'content-type': 'text/xml'}

        request = self.session.get(uri, verify=False, headers=headers)
        root = ET.fromstring(request.text)

        for infraRtDomP in root.iter("infraRtDomP"):
            string = infraRtDomP.get("dn")
            if re.findall(r'phys-.*?[/]\b', string):
                aaeps = re.findall(r'(?<=attentp-).*(?=])', string)
                phys_dom = re.findall(r'(?<=phys-).*(?=/rt)', string)
                self.policies_dict["AAEP " + aaeps[0]].append(phys_dom)
            elif re.findall(r'l3dom-.*?[/]\b', string):
                aaeps = re.findall(r'(?<=attentp-).*(?=])', string)
                l3_dom = re.findall(r'(?<=l3dom-).*(?=/rt)', string)
                self.policies_dict["AAEP " + aaeps[0]].append(l3_dom)
            elif re.findall(r'vmmp-.*?[/]\b', string):
                aaeps = re.findall(r'(?<=attentp-).*(?=])', string)
                vmm_dom = re.findall(r'(?<=vmmp-).*(?=/rt)', string)
                self.policies_dict["AAEP " + aaeps[0]].append(vmm_dom[0])
            else:
                continue

        for infraRsVlanNs in root.iter("infraRsVlanNs"):

            vl_pool_dn = infraRsVlanNs.get("tDn")
            phys_dom_dn = infraRsVlanNs.get("dn")

            if re.findall(r'(?<=phys-).*(?=/)', phys_dom_dn):
                phys_dom = re.findall(r'(?<=phys-).*(?=/)', phys_dom_dn)
                vlan_pool = re.findall(r'(?<=vlanns-\[).*(?=])', vl_pool_dn)
                self.policies_dict[vlan_pool[0]].append(phys_dom)
            elif re.findall(r'(?<=ledom-).*(?=/)', phys_dom_dn):
                l3_dom = re.findall(r'(?<=l3dom-).*(?=/)', phys_dom_dn)
                vlan_pool = re.findall(r'(?<=vlanns-\[).*(?=])', vl_pool_dn)
                self.policies_dict[vlan_pool[0]].append(l3_dom)
            elif re.findall(r'(?<=vmmp-).*(?=/)', phys_dom_dn):
                vmm_dom = re.findall(r'(?<=vmmp-).*(?=/)', phys_dom_dn)
                vlan_pool = re.findall(r'(?<=vlanns-\[).*(?=])', vl_pool_dn)
                self.policies_dict[vlan_pool[0]].append(vmm_dom[0])
            else:
                continue

        return self.policies_dict

    def infr(self, **kwargs):


        """Takes in pod number , and return all information about the fabric hardware. Greate for TAC use"""

        pod_num = kwargs["pod"]
        pod_number = "pod-{}".format(pod_num)

        uri = "https://{}/api/node/mo/topology/{}.xml?query-target=children".format(self.apic, pod_number)

        request = self.session.get(uri, verify=False)
        root = ET.fromstring(request.text)

        for fabricNode in root.iter("fabricNode"):
            fabric_node = fabricNode.get("name")
            model_node = fabricNode.get("model")
            serial_node = fabricNode.get("serial")
            self.device_info.append(fabric_node)
            self.device_info.append(model_node)
            self.device_info.append(serial_node)

        if not self.device_info:
            return "No Infrastructor Information"
        else:
            return self.device_info

    def view_tenants(self):

        """Returns ACI Tenant from the arbitrary APIC"""

        uri = "https://{}/api/class/fvTenant.json".format(self.apic)

        request = self.session.get(uri, verify=False)
        response_dict = request.json()
        total_count = int(response_dict["totalCount"])

        try:
            index = 0
            for i in range(0, total_count):
                self.tenant_array.append(response_dict["imdata"][index]["fvTenant"]["attributes"]["name"])
                index = index + 1
        except IndexError:
            pass

        return self.tenant_array

    def subnet_finder(self, **kwargs):

        """ Takes in kwarg subnet and finds all details about the subnet (BD, Tenant, scope etc."""


        self.view_tenants()

        uri = "https://{}/api/class/fvBD.xml?query-target=subtree".format(self.apic)
        request = self.session.get(uri, verify=False)
        root = ET.fromstring(request.text)

        for fvSubnet in root.iter("fvSubnet"):
            location = fvSubnet.get("dn")
            ip = fvSubnet.get("ip")
            if kwargs["subnet"] in ip:
                gps = location
                gps_ip = ip
                scope = fvSubnet.get("scope")

        try:
            for fvBD in root.iter("fvBD"):
                bridge_domain = fvBD.get("name")
                if re.findall('[?<=/BD-]' + bridge_domain + '(?=/)', gps):
                    gps_bd = bridge_domain
                    uni_route = fvBD.get("unicastRoute")
                    unkwn_uni = fvBD.get("unkMacUcastAct")

            for fvRsCtx in root.iter("fvRsCtx"):
                vrf = fvRsCtx.get("tnFvCtxName")
                location = fvRsCtx.get("dn")
                if re.findall('[?<=/BD-]' + gps_bd + '(?=/)', location):
                    gps_vrf = vrf

            aps = []
            epgs = []

            for fvRtBd in root.iter("fvRtBd"):
                dn = fvRtBd.get("dn")
                if re.findall('[?<=/BD-]' + gps_bd + '(?=/)', dn):
                    ap = re.findall(r'(?<=ap-).*(?=/ep)', dn)
                    aps.append(ap[0])
                    epg = re.findall(r'(?<=epg-).*(?=\])', dn)
                    epgs.append(epg[0])
                else:
                    pass

            for fvRsBDToOut in root.iter("fvRsBDToOut"):
                dn = fvRsBDToOut.get("dn")
                if re.findall('[?<=/BD-]' + gps_bd + '(?=/)', dn):
                    if not fvRsBDToOut.get("tnL3extOutName"):
                        l3out = "N/A"
                    else:
                        l3out = fvRsBDToOut.get("tnL3extOutName")

            for tenant in self.tenant_array:
                if tenant in gps:
                    gps_tenant = tenant
                else:
                    continue

            unpack_ap = [i for i in aps]
            unpack_epg = [i for i in epgs]

        except UnboundLocalError:
            pass

        return gps_ip, gps_tenant, gps_bd, vrf, l3out, uni_route, scope, unkwn_uni, unpack_ap, unpack_epg

    def tenant_vrf(self, **kwargs):

        """View Tenant vrf, return Tenant vrf names"""

        vrf_dict = collections.defaultdict(list)
        uri = "https://{}/api/node/mo/uni/tn-{}.json?query-target=children&target-subtree-class=fvCtx"\
                                                                    .format(self.apic, kwargs["tenant"])
        request = self.session.get(uri, verify=False)
        response = json.loads(request.text)

        try:
            index = 0
            for i in range(0, 100):
                vrf_dict["vrf"].append(response["imdata"][index]["fvCtx"]["attributes"]["name"])
                index = index + 1
        except IndexError:
            pass

        return vrf_dict

    def view_bd(self, **kwargs):

        """View Bridge domains of a Tenant, returns bridge domain names"""

        bd_dict = collections.defaultdict(list)
        uri = "https://{}/api/node/mo/uni/tn-{}.json?query-target=children&target-subtree-class=fvBD"\
                                                                    .format(self.apic, kwargs["tenant"])
        request = self.session.get(uri, verify=False)
        response = json.loads(request.text)
        total_count = int(response["totalCount"])

        index = 0
        for i in range(0, total_count):
            bd_dict["name"].append(response["imdata"][index]["fvBD"]["attributes"]["name"])
            index = index + 1

        return bd_dict

    def view_app_profiles(self, **kwargs):

        """View Application profiles of a particular Tenant, return App profiles"""

        app_dict = collections.defaultdict(list)
        uri = "https://{}/api/node/mo/uni/tn-{}.json?query-target=children&target-subtree-class=fvAp"\
                                                                    .format(self.apic, kwargs["tenant"])
        request = self.session.get(uri, verify=False)
        response = json.loads(request.text)
        total_count = int(response["totalCount"])

        index = 0
        for i in range(0, total_count):
            app_dict["name"].append(response["imdata"][index]["fvAp"]["attributes"]["name"])
            index = index + 1

        return app_dict

    def view_epgs(self, **kwargs):

        """View endpoint groups of a particular Tenant-App profile, returns EPG names"""

        epg_dict = collections.defaultdict(list)
        uri = "https://{}/api/node/mo/uni/tn-{}/ap-{}.json?query-target=children&target-subtree-class=fvAEPg"\
                                                            .format(self.apic, kwargs["tenant"], kwargs["app"])
        request = self.session.get(uri, verify=False)
        response = json.loads(request.text)
        total_count = int(response["totalCount"])

        index = 0
        for i in range(0, total_count):
            epg_dict["name"].append(response["imdata"][index]["fvAEPg"]["attributes"]["name"])
            index = index + 1

        return epg_dict

    def enpoint_tracker(self, **kwargs):

        """This method take in a IP or MAC address and returns the endpoint data. Return string if no endpoint is found"""

        try:
            ipaddress.IPv4Address(kwargs["endpoint"])
            uri = "https://%s" % self.apic + "/api/node/class/fvCEp.xml?rsp-subtree=full&rsp-subtree-include=" \
                                     "required&rsp-subtree-filter=eq(fvIp.addr," + "\"%s\"" % kwargs["endpoint"]

        except ValueError:
            uri = "https://%s" % self.apic + "/api/node/class/fvCEp.xml?rsp-subtree=full&rsp-subtree-class=" \
                  "fvCEp,fvRsCEpToPathEp,fvIp,fvRsHyper,fvRsToNic,fvRsToVm&query-target-filter=eq(fvCEp.mac," \
                  + "\"%s\"" % kwargs["endpoint"]

        request = self.session.get(uri, verify=False)
        root = ET.fromstring(request.text)

        for fvCEp in root.iter("fvCEp"):
            print(fvCEp)
            ep_name = fvCEp.get("name")
            ep_mac = fvCEp.get("mac")
            encap = fvCEp.get("encap")
            ep_loc = fvCEp.get("dn")
            ep_ip = fvCEp.get("ip")

            endpoint = ("Name: {0:20}\nEP: {1:<20}\nEncapsulation: {2:<20}\nLocation: {3:<20}\nIP: {4:<20}"
                                                                .format(ep_name,ep_mac,encap,ep_loc,ep_ip))


        try:
            return endpoint
        except UnboundLocalError:
            return  "Endpoint Not Found"


Collects authentication information from user, returns session if successfull, or response if not APIC authentication method. Takes username, password, apic kwargs and returns session Get fabric vlan pools, return pool in a dictionary data structure Takes in the vlan encapsulation, intiaates vlan_pool(0 and policy_mappings. Calls _find_encap_comiple to fin fabric information about the encapsulation
         Returns a series of list to the caller This method is for local use only. It works with vlan_pool() to produce a series of list and return them to the call find_encap Maps AAEPS, Vlan Pools, and phys/vmm/routed domain. Return dictionary data structure Takes in pod number , and return all information about the fabric hardware. Greate for TAC use Returns ACI Tenant from the arbitrary APIC Takes in kwarg subnet and finds all details about the subnet (BD, Tenant, scope etc. View Tenant vrf, return Tenant vrf names View Bridge domains of a Tenant, returns bridge domain names View Application profiles of a particular Tenant, return App profiles View endpoint groups of a particular Tenant-App profile, returns EPG names This method take in a IP or MAC address and returns the endpoint data. Return string if no endpoint is found