def create_random_tenant_config(config):
    # Create the Tenant object
    tenant_prefix = 'acitoolkitrandomized-'
    tenant_name = tenant_prefix + random_string(random_number(1, 63 - len(tenant_prefix)))
    tenant = Tenant(tenant_name)

    # Create some number of BridgeDomains
    bridge_domains = []
    for i in range(0, random_number(0, random_number(int(config.get('BridgeDomains', 'Minimum')),
                                                     int(config.get('BridgeDomains', 'Maximum'))))):
        bd = BridgeDomain(random_string(random_number(1, 64)), tenant)
        # Randomly choose settings for the BridgeDomain
        if config.get('BridgeDomains', 'AllowFloodUnkMacUcast').lower() == 'true':
            bd.set_unknown_mac_unicast(random.choice(['proxy', 'flood']))
        if config.get('BridgeDomains', 'AllowOptimizedFloodUnknownMcast').lower() == 'true':
            bd.set_unknown_multicast(random.choice(['flood', 'opt-flood']))
        if config.get('BridgeDomains', 'AllowArpFlood').lower() == 'true':
            bd.set_arp_flood(random.choice(['yes', 'no']))
        if config.get('BridgeDomains', 'AllowDisableUnicastRoute').lower() == 'true':
            bd.set_unicast_route(random.choice(['yes', 'no']))
        if config.get('BridgeDomains', 'AllowNonDefaultMultiDstPkt').lower() == 'true':
            bd.set_multidestination(random.choice(['drop', 'bd-flood', 'encap-flood']))
        bridge_domains.append(bd)

    # Create some number of Contexts
    contexts = []
    for i in range(0, random_number(0, random_number(int(config.get('Contexts', 'Minimum')),
                                                     int(config.get('Contexts', 'Maximum'))))):
        context = Context(random_string(random_number(1, 64)), tenant)
        if config.get('Contexts', 'AllowUnenforced').lower() == 'true':
            context.set_allow_all(random.choice([True, False]))
        contexts.append(context)

    # Randomly associate BridgeDomains with the Contexts (or use default)
    for bd in bridge_domains:
        if random.choice([True, True, False]) and len(contexts):
            bd.add_context(random.choice(contexts))

    # Create some number of Application Profiles
    apps = []
    for i in range(0, random_number(0, random_number(int(config.get('ApplicationProfiles', 'Minimum')),
                                                     int(config.get('ApplicationProfiles', 'Maximum'))))):
        app = AppProfile(random_string(random_number(1, 64)), tenant)
        apps.append(app)

    # Create some number of EPGs and place in AppProfiles
    epgs = []
    if len(apps):
        for i in range(0, random_number(0, random_number(int(config.get('EPGs', 'Minimum')),
                                                         int(config.get('EPGs', 'Maximum'))))):
            epg = EPG(random_string(random_number(1, 64)), random.choice(apps))
            epgs.append(epg)

    # Randomly associate the EPGs to BridgeDomains
    for epg in epgs:
        if random_number(0, 9) == 1 and len(bridge_domains):  # 1 in 10 chance for no bridgedomain
            epg.add_bd(random.choice(bridge_domains))

    # Create some filters
    filters = []
    for i in range(0, random_number(0, random_number(int(config.get('Filters', 'Minimum')),
                                                     int(config.get('Filters', 'Maximum'))))):
        filter = Filter(random_string(random_number(1, 64)), tenant)
        filters.append(filter)

    # Create some filter entries
    filter_entries = []
    ip_protocols = {
        'icmp': '1',
        'igmp': '2',
        'tcp': '6',
        'egp': '8',
        'igp': '9',
        'udp': '17',
        'icmpv6': '58',
        'eigrp': '88',
        'ospfigp': '89',
        'pim': '103',
        'l2tp': '115'
    }
    if len(filters):
        for i in range(0, random_number(0, random_number(int(config.get('FilterEntries', 'Minimum')),
                                                         int(config.get('FilterEntries', 'Maximum'))))):
            applyToFrag='0'
            arpOpc = '0'
            dFromPort = '0'
            dToPort = '0'
            prot = '0'
            sFromPort = '0'
            sToPort = '0'
            tcpRules = '0'
            stateful = '0'
            if random_chance(20):  # 20% chance of ARP
                arpOpc = random.choice(['req', 'reply'])
                etherT = 'arp'
            elif random_chance(25):  # 20% of remaining 80% is non-IP (16% of total)
                ethertype_choices = ['trill', 'mpls_ucast', 'mac_security', 'fcoe']
                # if not filter.has_wildcard_entry():
                #     ethertype_choices += ['0']
                etherT = random.choice(ethertype_choices)
            else:  # remaining is IP
                applyToFrag = random.choice(['0', '1'])
                etherT = 'ip'
                if random_chance(20):  # Choose more obscure protocols 20% of the time
                    prot = ip_protocols[random.choice(['igmp', 'egp', 'igp', 'eigrp', 'ospfigp', 'pim', 'l2tp'])]
                else:
                    prot = ip_protocols[random.choice(['icmp', 'tcp', 'udp', 'icmpv6'])]
                    if prot == ip_protocols['icmp']:
                        pass
                    elif prot == ip_protocols['icmpv6']:
                        pass
                    else:
                        # Remainder is TCP or UDP
                        dFromPort, dToPort = random_range(0, 65535)
                        sFromPort, sToPort = random_range(0, 65535)
                        if dFromPort != '0' or dToPort != '0' or sFromPort != '0' or sToPort != '0':
                            applyToFrag = '0'
                        if prot == ip_protocols['tcp']:
                            if random_chance(30):
                                tcpRules = random.choice(['est', 'syn', 'ack', 'fin'])
            parent = random.choice(filters)
            if not parent.has_entry(applyToFrag, arpOpc, dFromPort, dToPort, etherT, prot, sFromPort, sToPort,
                                    tcpRules, stateful):
                filter_entry = FilterEntry(name=random_string(random_number(1, 64)),
                                           parent=random.choice(filters),
                                           applyToFrag=applyToFrag,
                                           arpOpc=arpOpc,
                                           dFromPort=dFromPort,
                                           dToPort=dToPort,
                                           etherT=etherT,
                                           prot=prot,
                                           sFromPort=sFromPort,
                                           sToPort=sToPort,
                                           tcpRules=tcpRules,
                                           stateful=stateful)

    # Create some Contracts
    contracts = []
    for i in range(0, random_number(0, random_number(int(config.get('Contracts', 'Minimum')),
                                                     int(config.get('Contracts', 'Maximum'))))):
        contract = Contract(random_string(random_number(1, 64)), tenant)
        contracts.append(contract)

    # Create some ContractSubjects
    if len(contracts):
        contract_subjects = []
        for i in range(0, random_number(0, random_number(int(config.get('ContractSubjects', 'Minimum')),
                                                         int(config.get('ContractSubjects', 'Maximum'))))):
            contract_subject = ContractSubject(random_string(random_number(1, 64)), random.choice(contracts))
            contract_subjects.append(contract_subject)

    # Randomly assign Filters to the ContractSubjects
    for filter in filters:
        if len(contracts) and len(contract_subjects):
            already_picked = []
            # Pick an arbitrary number of Subjects
            for i in range(0, random_number(1, len(contract_subjects))):
                pick = random_number(0, len(contract_subjects) - 1)
                # Only choose each subject at most once
                if pick not in already_picked:
                    contract_subjects[pick].add_filter(filter)
                    already_picked.append(pick)

    # Randomly provide and consume the Contracts from the EPGs
    for action in ['provide', 'consume']:
        for epg in epgs:
            already_picked = []
            for i in range(0, random_number(0, len(contracts))):
                pick = random_number(0, len(contracts) - 1)
                if pick not in already_picked:
                    getattr(epg, action)(contracts[pick])
                    already_picked.append(pick)

    return tenant
    def create_random_tenant(self, interfaces=[]):
        max_string_length = int(
            self._config.get('GlobalDefaults', 'MaximumStringLength'))
        # Create the Tenant object
        tenant_prefix = 'acitoolkitrandomized-'
        tenant_name_len = random_number(1,
                                        max_string_length - len(tenant_prefix))
        tenant_name = tenant_prefix + random_string(tenant_name_len)
        tenant = Tenant(tenant_name)

        # Create some number of BridgeDomains
        bridge_domains = []
        maximum_bds = int(self._config.get('BridgeDomains', 'Maximum'))
        if maximum_bds > self._global_limits.max_bds:
            maximum_bds = self._global_limits.max_bds
        for i in range(
                0,
                random_number(
                    0,
                    random_number(
                        int(self._config.get('BridgeDomains', 'Minimum')),
                        maximum_bds))):
            self._global_limits.max_bds -= 1
            bd = BridgeDomain(
                random_string(random_number(1, max_string_length)), tenant)
            # Randomly choose settings for the BridgeDomain
            if self._config.get('BridgeDomains',
                                'AllowFloodUnkMacUcast').lower() == 'true':
                bd.set_unknown_mac_unicast(random.choice(['proxy', 'flood']))
            if self._config.get(
                    'BridgeDomains',
                    'AllowOptimizedFloodUnknownMcast').lower() == 'true':
                bd.set_unknown_multicast(random.choice(['flood', 'opt-flood']))
            if self._config.get('BridgeDomains',
                                'AllowArpFlood').lower() == 'true':
                bd.set_arp_flood(random.choice(['yes', 'no']))
            if self._config.get('BridgeDomains',
                                'AllowDisableUnicastRoute').lower() == 'true':
                bd.set_unicast_route(random.choice(['yes', 'no']))
            if self._config.get(
                    'BridgeDomains',
                    'AllowNonDefaultMultiDstPkt').lower() == 'true':
                bd.set_multidestination(
                    random.choice(['drop', 'bd-flood', 'encap-flood']))
            bridge_domains.append(bd)

        # Create some number of Contexts
        contexts = []
        max_contexts = int(self._config.get('Contexts', 'Maximum'))
        if max_contexts > self._global_limits.max_contexts:
            max_contexts = self._global_limits.max_contexts
        if max_contexts > int(self._config.get('Contexts',
                                               'MaximumPerTenant')):
            max_contexts = int(self._config.get('Contexts',
                                                'MaximumPerTenant'))
        for i in range(
                0,
                random_number(
                    0,
                    random_number(int(self._config.get('Contexts', 'Minimum')),
                                  max_contexts))):
            context = Context(
                random_string(random_number(1, max_string_length)), tenant)
            self._global_limits.max_contexts -= 1
            if self._config.get('Contexts',
                                'AllowUnenforced').lower() == 'true':
                context.set_allow_all(random.choice([True, False]))
            contexts.append(context)

        # Randomly associate BridgeDomains with the Contexts (or use default)
        for bd in bridge_domains:
            if random.choice([True, True, False]) and len(contexts):
                bd.add_context(random.choice(contexts))

        # Create some number of Application Profiles
        apps = []
        for i in range(
                0,
                random_number(
                    0,
                    random_number(
                        int(self._config.get('ApplicationProfiles',
                                             'Minimum')),
                        int(self._config.get('ApplicationProfiles',
                                             'Maximum'))))):
            app = AppProfile(
                random_string(random_number(1, max_string_length)), tenant)
            apps.append(app)

        # Create some number of EPGs and place in AppProfiles
        epgs = []
        max_epgs = int(self._config.get('EPGs', 'Maximum'))
        if max_epgs > self._global_limits.max_epgs:
            max_epgs = self._global_limits.max_epgs
        if len(apps):
            for i in range(
                    0,
                    random_number(
                        0,
                        random_number(int(self._config.get('EPGs', 'Minimum')),
                                      max_epgs))):
                epg = EPG(random_string(random_number(1, max_string_length)),
                          random.choice(apps))
                self._global_limits.max_epgs -= 1
                epgs.append(epg)

        # Randomly associate the EPGs to BridgeDomains
        bd_epg_count = [0] * len(bridge_domains)
        for epg in epgs:
            if random_number(0, 9) == 1 or len(
                    bridge_domains) == 0:  # 1 in 10 chance for no bridgedomain
                continue
            keep_trying = 100
            while keep_trying:
                bd_choice = random_number(0, len(bridge_domains) - 1)
                if bd_epg_count[bd_choice] <= int(
                        self._config.get('BridgeDomains', 'MaximumEPGs')):
                    epg.add_bd(bridge_domains[bd_choice])
                    bd_epg_count[bd_choice] += 1
                    break
                else:
                    keep_trying -= 1

        # Randomly assign the EPGs to the interfaces provided
        interface_objs = {}
        for interface in interfaces:
            # Create the Interface objects
            interface_objs[interface] = Interface.create_from_name(interface)
        for epg in epgs:
            # Pick an interface
            interface_choice = random.choice(interfaces)
            # Pick a VLAN
            vlan_choice = 0
            keep_trying = 100
            while vlan_choice in self._interfaces[interface_choice]:
                vlan_choice = random_number(
                    int(self._config.get('VLANs', 'Minimum')),
                    int(self._config.get('VLANs', 'Maximum')))
                keep_trying -= 1
            if not keep_trying:
                continue
            # Create the VLAN interface
            vlan_intf = L2Interface(
                'vlan%s-on-%s' %
                (str(vlan_choice),
                 interface_objs[interface_choice].name.replace(' ', '')),
                'vlan', str(vlan_choice))
            # Attach the VLAN interface to the Interface object
            vlan_intf.attach(interface_objs[interface_choice])
            # Attach the EPG to the VLAN interface
            epg.attach(vlan_intf)

        # Create some filters
        filters = []
        max_filters = int(self._config.get('Filters', 'Maximum'))
        if max_filters > self._global_limits.max_filters:
            max_filters = self._global_limits.max_filters
        for i in range(
                0,
                random_number(
                    0,
                    random_number(int(self._config.get('Filters', 'Minimum')),
                                  max_filters))):
            filter = Filter(random_string(random_number(1, max_string_length)),
                            tenant)
            self._global_limits.max_filters -= 1
            filters.append(filter)

        # Create some filter entries
        filter_entries = []
        max_filter_entries = int(self._config.get('FilterEntries', 'Maximum'))
        if max_filter_entries > self._global_limits.max_filter_entries:
            max_filter_entries = self._global_limits.max_filter_entries
        ip_protocols = {
            'icmp': '1',
            'igmp': '2',
            'tcp': '6',
            'egp': '8',
            'igp': '9',
            'udp': '17',
            'icmpv6': '58',
            'eigrp': '88',
            'ospfigp': '89',
            'pim': '103',
            'l2tp': '115'
        }
        if len(filters):
            for i in range(
                    0,
                    random_number(
                        0,
                        random_number(
                            int(self._config.get('FilterEntries', 'Minimum')),
                            max_filter_entries))):
                applyToFrag = '0'
                arpOpc = '0'
                dFromPort = '0'
                dToPort = '0'
                prot = '0'
                sFromPort = '0'
                sToPort = '0'
                tcpRules = '0'
                stateful = '0'
                icmpv4T = 'not-given'
                icmpv6T = 'not-given'
                if random_chance(20):  # 20% chance of ARP
                    arpOpc = random.choice(['req', 'reply'])
                    etherT = 'arp'
                elif random_chance(
                        25):  # 20% of remaining 80% is non-IP (16% of total)
                    ethertype_choices = [
                        'trill', 'mpls_ucast', 'mac_security', 'fcoe'
                    ]
                    # if not filter.has_wildcard_entry():
                    #     ethertype_choices += ['0']
                    etherT = random.choice(ethertype_choices)
                else:  # remaining is IP
                    applyToFrag = random.choice(['0', '1'])
                    etherT = 'ip'
                    if random_chance(
                            20
                    ):  # Choose more obscure protocols 20% of the time
                        prot = ip_protocols[random.choice([
                            'igmp', 'egp', 'igp', 'eigrp', 'ospfigp', 'pim',
                            'l2tp'
                        ])]
                    else:
                        prot = ip_protocols[random.choice(
                            ['icmp', 'tcp', 'udp', 'icmpv6'])]
                        if prot == ip_protocols['icmp']:
                            icmpv4T = random.choice([
                                'echo-rep', 'dst-unreach', 'src-quench',
                                'echo', 'time-exceeded', 'unspecified',
                                'not-given'
                            ])
                        elif prot == ip_protocols['icmpv6']:
                            icmpv6T = random.choice([
                                'unspecified', 'dst-unreach', 'time-exceeded',
                                'echo-req', 'echo-rep', 'nbr-solicit',
                                'nbr-advert', 'redirect', 'not-given'
                            ])
                        else:
                            # Remainder is TCP or UDP
                            dFromPort, dToPort = random_range(0, 65535)
                            sFromPort, sToPort = random_range(0, 65535)
                            if dFromPort != '0' or dToPort != '0' or sFromPort != '0' or sToPort != '0':
                                applyToFrag = '0'
                            if prot == ip_protocols['tcp']:
                                # Randomly choose whether to specify tcpRules
                                if random_chance(30):
                                    # TODO: should actually take odds from the config file
                                    # Choose a random number of the possible tcpRules but
                                    # if est is chosen, then it must be the only tcpRule. Otherwise, APIC rejects it
                                    tcp_rule_choices = []
                                    tcp_rule_possibilities = [
                                        'est', 'syn', 'ack', 'fin', 'rst'
                                    ]
                                    tcp_choice = random.choice(
                                        tcp_rule_possibilities)
                                    tcp_rule_choices.append(tcp_choice)
                                    while tcp_choice != 'est':
                                        tcp_choice = random.choice(
                                            tcp_rule_possibilities)
                                        if tcp_choice != 'est' and tcp_choice not in tcp_rule_choices:
                                            tcp_rule_choices.append(tcp_choice)
                                    tcpRules = ''
                                    for tcp_choice in tcp_rule_choices:
                                        tcpRules += str(tcp_choice) + ','
                parent = random.choice(filters)
                if not parent.has_entry(applyToFrag, arpOpc, dFromPort,
                                        dToPort, etherT, prot, sFromPort,
                                        sToPort, tcpRules, stateful, icmpv4T,
                                        icmpv6T):
                    filter_entry = FilterEntry(name=random_string(
                        random_number(1, max_string_length)),
                                               parent=random.choice(filters),
                                               applyToFrag=applyToFrag,
                                               arpOpc=arpOpc,
                                               dFromPort=dFromPort,
                                               dToPort=dToPort,
                                               etherT=etherT,
                                               prot=prot,
                                               sFromPort=sFromPort,
                                               sToPort=sToPort,
                                               tcpRules=tcpRules,
                                               stateful=stateful,
                                               icmpv4T=icmpv4T,
                                               icmpv6T=icmpv6T)
                self._global_limits.max_filter_entries -= 1

        # Create some Contracts
        contracts = []
        max_contracts = int(self._config.get('Contracts', 'Maximum'))
        if max_contracts > self._global_limits.max_contracts:
            max_contracts = self._global_limits.max_contracts
        for i in range(
                0,
                random_number(
                    0,
                    random_number(
                        int(self._config.get('Contracts', 'Minimum')),
                        max_contracts))):
            contract = Contract(
                random_string(random_number(1, max_string_length)), tenant)
            self._global_limits.max_contracts -= 1
            contracts.append(contract)

        # Create some ContractSubjects
        if len(contracts):
            contract_subjects = []
            for i in range(
                    0,
                    random_number(
                        0,
                        random_number(
                            int(self._config.get('ContractSubjects',
                                                 'Minimum')),
                            int(self._config.get('ContractSubjects',
                                                 'Maximum'))))):
                contract_subject = ContractSubject(
                    random_string(random_number(1, max_string_length)),
                    random.choice(contracts))
                contract_subjects.append(contract_subject)

        # Randomly assign Filters to the ContractSubjects
        for filter in filters:
            if len(contracts) and len(contract_subjects):
                already_picked = []
                # Pick an arbitrary number of Subjects
                for i in range(0, random_number(1, len(contract_subjects))):
                    pick = random_number(0, len(contract_subjects) - 1)
                    # Only choose each subject at most once
                    if pick not in already_picked:
                        contract_subjects[pick].add_filter(filter)
                        already_picked.append(pick)

        # Randomly provide and consume the Contracts from the EPGs
        for action, max_num_epgs in [
            ('provide',
             int(self._config.get('Contracts', 'MaximumProvidingEPGs'))),
            ('consume',
             int(self._config.get('Contracts', 'MaximumConsumingEPGs')))
        ]:
            contract_count = [0] * len(contracts)
            for epg in epgs:
                already_picked = []
                for i in range(0, random_number(0, len(contracts))):
                    keep_trying = 20  # Try 20 times to randomly pick a contract
                    while keep_trying:
                        pick = random_number(0, len(contracts) - 1)
                        if pick not in already_picked and contract_count[
                                pick] < max_num_epgs:
                            getattr(epg, action)(contracts[pick])
                            already_picked.append(pick)
                            contract_count[pick] += 1
                            keep_trying = 0
                        else:
                            keep_trying -= 1
        return tenant
    def create_random_tenant(self, interfaces=[]):
        max_string_length = int(self._config.get('GlobalDefaults', 'MaximumStringLength'))
        # Create the Tenant object
        tenant_prefix = 'acitoolkitrandomized-'
        tenant_name_len = random_number(1, max_string_length - len(tenant_prefix))
        tenant_name = tenant_prefix + random_string(tenant_name_len)
        tenant = Tenant(tenant_name)

        # Create some number of BridgeDomains
        bridge_domains = []
        maximum_bds = int(self._config.get('BridgeDomains', 'Maximum'))
        if maximum_bds > self._global_limits.max_bds:
            maximum_bds = self._global_limits.max_bds
        for i in range(0, random_number(0, random_number(int(self._config.get('BridgeDomains', 'Minimum')),
                                                         maximum_bds))):
            self._global_limits.max_bds -= 1
            bd = BridgeDomain(random_string(random_number(1, max_string_length)), tenant)
            # Randomly choose settings for the BridgeDomain
            if self._config.get('BridgeDomains', 'AllowFloodUnkMacUcast').lower() == 'true':
                bd.set_unknown_mac_unicast(random.choice(['proxy', 'flood']))
            if self._config.get('BridgeDomains', 'AllowOptimizedFloodUnknownMcast').lower() == 'true':
                bd.set_unknown_multicast(random.choice(['flood', 'opt-flood']))
            if self._config.get('BridgeDomains', 'AllowArpFlood').lower() == 'true':
                bd.set_arp_flood(random.choice(['yes', 'no']))
            if self._config.get('BridgeDomains', 'AllowDisableUnicastRoute').lower() == 'true':
                bd.set_unicast_route(random.choice(['yes', 'no']))
            if self._config.get('BridgeDomains', 'AllowNonDefaultMultiDstPkt').lower() == 'true':
                bd.set_multidestination(random.choice(['drop', 'bd-flood', 'encap-flood']))
            bridge_domains.append(bd)

        # Create some number of Contexts
        contexts = []
        max_contexts = int(self._config.get('Contexts', 'Maximum'))
        if max_contexts > self._global_limits.max_contexts:
            max_contexts = self._global_limits.max_contexts
        if max_contexts > int(self._config.get('Contexts', 'MaximumPerTenant')):
            max_contexts = int(self._config.get('Contexts', 'MaximumPerTenant'))
        for i in range(0, random_number(0, random_number(int(self._config.get('Contexts', 'Minimum')),
                                                         max_contexts))):
            context = Context(random_string(random_number(1, max_string_length)), tenant)
            self._global_limits.max_contexts -= 1
            if self._config.get('Contexts', 'AllowUnenforced').lower() == 'true':
                context.set_allow_all(random.choice([True, False]))
            contexts.append(context)

        # Randomly associate BridgeDomains with the Contexts (or use default)
        for bd in bridge_domains:
            if random.choice([True, True, False]) and len(contexts):
                bd.add_context(random.choice(contexts))

        # Create some number of Application Profiles
        apps = []
        for i in range(0, random_number(0, random_number(int(self._config.get('ApplicationProfiles', 'Minimum')),
                                                         int(self._config.get('ApplicationProfiles', 'Maximum'))))):
            app = AppProfile(random_string(random_number(1, max_string_length)), tenant)
            apps.append(app)

        # Create some number of EPGs and place in AppProfiles
        epgs = []
        max_epgs = int(self._config.get('EPGs', 'Maximum'))
        if max_epgs > self._global_limits.max_epgs:
            max_epgs = self._global_limits.max_epgs
        if len(apps):
            for i in range(0, random_number(0, random_number(int(self._config.get('EPGs', 'Minimum')),
                                                             max_epgs))):
                epg = EPG(random_string(random_number(1, max_string_length)), random.choice(apps))
                self._global_limits.max_epgs -= 1
                epgs.append(epg)

        # Randomly associate the EPGs to BridgeDomains
        bd_epg_count = [0] * len(bridge_domains)
        for epg in epgs:
            if random_number(0, 9) == 1 or len(bridge_domains) == 0:   # 1 in 10 chance for no bridgedomain
                continue
            keep_trying = 100
            while keep_trying:
                bd_choice = random_number(0, len(bridge_domains) - 1)
                if bd_epg_count[bd_choice] <= int(self._config.get('BridgeDomains', 'MaximumEPGs')):
                    epg.add_bd(bridge_domains[bd_choice])
                    bd_epg_count[bd_choice] += 1
                    break
                else:
                    keep_trying -= 1

        # Randomly assign the EPGs to the interfaces provided
        interface_objs = {}
        for interface in interfaces:
            # Create the Interface objects
            interface_objs[interface] = Interface.create_from_name(interface)
        for epg in epgs:
            # Pick an interface
            interface_choice = random.choice(interfaces)
            # Pick a VLAN
            vlan_choice = 0
            keep_trying = 100
            while vlan_choice in self._interfaces[interface_choice]:
                vlan_choice = random_number(int(self._config.get('VLANs', 'Minimum')),
                                            int(self._config.get('VLANs', 'Maximum')))
                keep_trying -= 1
            if not keep_trying:
                continue
            # Create the VLAN interface
            vlan_intf = L2Interface('vlan%s-on-%s' % (str(vlan_choice),
                                                      interface_objs[interface_choice].name.replace(' ', '')),
                                    'vlan',
                                    str(vlan_choice))
            # Attach the VLAN interface to the Interface object
            vlan_intf.attach(interface_objs[interface_choice])
            # Attach the EPG to the VLAN interface
            epg.attach(vlan_intf)

        # Create some filters
        filters = []
        max_filters = int(self._config.get('Filters', 'Maximum'))
        if max_filters > self._global_limits.max_filters:
            max_filters = self._global_limits.max_filters
        for i in range(0, random_number(0, random_number(int(self._config.get('Filters', 'Minimum')),
                                                         max_filters))):
            filter = Filter(random_string(random_number(1, max_string_length)), tenant)
            self._global_limits.max_filters -= 1
            filters.append(filter)

        # Create some filter entries
        filter_entries = []
        max_filter_entries = int(self._config.get('FilterEntries', 'Maximum'))
        if max_filter_entries > self._global_limits.max_filter_entries:
            max_filter_entries = self._global_limits.max_filter_entries
        ip_protocols = {
            'icmp': '1',
            'igmp': '2',
            'tcp': '6',
            'egp': '8',
            'igp': '9',
            'udp': '17',
            'icmpv6': '58',
            'eigrp': '88',
            'ospfigp': '89',
            'pim': '103',
            'l2tp': '115'
        }
        if len(filters):
            for i in range(0, random_number(0, random_number(int(self._config.get('FilterEntries', 'Minimum')),
                                                             max_filter_entries))):
                applyToFrag = '0'
                arpOpc = '0'
                dFromPort = '0'
                dToPort = '0'
                prot = '0'
                sFromPort = '0'
                sToPort = '0'
                tcpRules = '0'
                stateful = '0'
                icmpv4T = 'not-given'
                icmpv6T = 'not-given'
                if random_chance(20):  # 20% chance of ARP
                    arpOpc = random.choice(['req', 'reply'])
                    etherT = 'arp'
                elif random_chance(25):  # 20% of remaining 80% is non-IP (16% of total)
                    ethertype_choices = ['trill', 'mpls_ucast', 'mac_security', 'fcoe']
                    # if not filter.has_wildcard_entry():
                    #     ethertype_choices += ['0']
                    etherT = random.choice(ethertype_choices)
                else:  # remaining is IP
                    applyToFrag = random.choice(['0', '1'])
                    etherT = 'ip'
                    if random_chance(20):  # Choose more obscure protocols 20% of the time
                        prot = ip_protocols[random.choice(['igmp', 'egp', 'igp', 'eigrp', 'ospfigp', 'pim', 'l2tp'])]
                    else:
                        prot = ip_protocols[random.choice(['icmp', 'tcp', 'udp', 'icmpv6'])]
                        if prot == ip_protocols['icmp']:
                            icmpv4T = random.choice(['echo-rep', 'dst-unreach', 'src-quench', 'echo',
                                                     'time-exceeded', 'unspecified', 'not-given'])
                        elif prot == ip_protocols['icmpv6']:
                            icmpv6T = random.choice(['unspecified', 'dst-unreach', 'time-exceeded',
                                                     'echo-req', 'echo-rep', 'nbr-solicit', 'nbr-advert',
                                                     'redirect', 'not-given'])
                        else:
                            # Remainder is TCP or UDP
                            dFromPort, dToPort = random_range(0, 65535)
                            sFromPort, sToPort = random_range(0, 65535)
                            if dFromPort != '0' or dToPort != '0' or sFromPort != '0' or sToPort != '0':
                                applyToFrag = '0'
                            if prot == ip_protocols['tcp']:
                                # Randomly choose whether to specify tcpRules
                                if random_chance(30):
                                    # TODO: should actually take odds from the config file
                                    # Choose a random number of the possible tcpRules but
                                    # if est is chosen, then it must be the only tcpRule. Otherwise, APIC rejects it
                                    tcp_rule_choices = []
                                    tcp_rule_possibilities = ['est', 'syn', 'ack', 'fin', 'rst']
                                    tcp_choice = random.choice(tcp_rule_possibilities)
                                    tcp_rule_choices.append(tcp_choice)
                                    while tcp_choice != 'est':
                                        tcp_choice = random.choice(tcp_rule_possibilities)
                                        if tcp_choice != 'est' and tcp_choice not in tcp_rule_choices:
                                            tcp_rule_choices.append(tcp_choice)
                                    tcpRules = ''
                                    for tcp_choice in tcp_rule_choices:
                                        tcpRules += str(tcp_choice) + ','
                parent = random.choice(filters)
                if not parent.has_entry(applyToFrag, arpOpc, dFromPort, dToPort, etherT, prot, sFromPort, sToPort,
                                        tcpRules, stateful, icmpv4T, icmpv6T):
                    filter_entry = FilterEntry(name=random_string(random_number(1, max_string_length)),
                                               parent=random.choice(filters),
                                               applyToFrag=applyToFrag,
                                               arpOpc=arpOpc,
                                               dFromPort=dFromPort,
                                               dToPort=dToPort,
                                               etherT=etherT,
                                               prot=prot,
                                               sFromPort=sFromPort,
                                               sToPort=sToPort,
                                               tcpRules=tcpRules,
                                               stateful=stateful,
                                               icmpv4T=icmpv4T,
                                               icmpv6T=icmpv6T)
                self._global_limits.max_filter_entries -= 1

        # Create some Contracts
        contracts = []
        max_contracts = int(self._config.get('Contracts', 'Maximum'))
        if max_contracts > self._global_limits.max_contracts:
            max_contracts = self._global_limits.max_contracts
        for i in range(0, random_number(0, random_number(int(self._config.get('Contracts', 'Minimum')),
                                                         max_contracts))):
            contract = Contract(random_string(random_number(1, max_string_length)), tenant)
            self._global_limits.max_contracts -= 1
            contracts.append(contract)

        # Create some ContractSubjects
        if len(contracts):
            contract_subjects = []
            for i in range(0, random_number(0, random_number(int(self._config.get('ContractSubjects', 'Minimum')),
                                                             int(self._config.get('ContractSubjects', 'Maximum'))))):
                contract_subject = ContractSubject(random_string(random_number(1, max_string_length)), random.choice(contracts))
                contract_subjects.append(contract_subject)

        # Randomly assign Filters to the ContractSubjects
        for filter in filters:
            if len(contracts) and len(contract_subjects):
                already_picked = []
                # Pick an arbitrary number of Subjects
                for i in range(0, random_number(1, len(contract_subjects))):
                    pick = random_number(0, len(contract_subjects) - 1)
                    # Only choose each subject at most once
                    if pick not in already_picked:
                        contract_subjects[pick].add_filter(filter)
                        already_picked.append(pick)

        # Randomly provide and consume the Contracts from the EPGs
        for action, max_num_epgs in [('provide', int(self._config.get('Contracts', 'MaximumProvidingEPGs'))),
                                     ('consume', int(self._config.get('Contracts', 'MaximumConsumingEPGs')))]:
            contract_count = [0] * len(contracts)
            for epg in epgs:
                already_picked = []
                for i in range(0, random_number(0, len(contracts))):
                    keep_trying = 20  # Try 20 times to randomly pick a contract
                    while keep_trying:
                        pick = random_number(0, len(contracts) - 1)
                        if pick not in already_picked and contract_count[pick] < max_num_epgs:
                            getattr(epg, action)(contracts[pick])
                            already_picked.append(pick)
                            contract_count[pick] += 1
                            keep_trying = 0
                        else:
                            keep_trying -= 1
        return tenant
    def create_random_tenant(self, interfaces=[]):
        max_string_length = int(
            self._config.get('GlobalDefaults', 'MaximumStringLength'))
        # Create the Tenant object
        tenant_prefix = self._config.get('GlobalDefaults', 'TenantPrefix')
        tenant_name_len = random_number(1,
                                        max_string_length - len(tenant_prefix))
        tenant_name = tenant_prefix + random_string(tenant_name_len)
        tenant = Tenant(tenant_name)

        # Create some number of BridgeDomains
        bridge_domains = []
        maximum_bds = int(self._config.get('BridgeDomains', 'Maximum'))
        if maximum_bds > self._global_limits.max_bds:
            maximum_bds = self._global_limits.max_bds
        for i in range(
                0,
                random_number(
                    0,
                    random_number(
                        int(self._config.get('BridgeDomains', 'Minimum')),
                        maximum_bds))):
            self._global_limits.max_bds -= 1
            bd = BridgeDomain(
                random_string(random_number(1, max_string_length)), tenant)
            # Randomly choose settings for the BridgeDomain
            if self._config.get('BridgeDomains',
                                'AllowFloodUnkMacUcast').lower() == 'true':
                bd.set_unknown_mac_unicast(
                    random.choice(
                        ast.literal_eval(
                            self._config.get('BridgeDomainSettings',
                                             'UnknownMacUnicast'))))
            if self._config.get(
                    'BridgeDomains',
                    'AllowOptimizedFloodUnknownMcast').lower() == 'true':
                bd.set_unknown_multicast(
                    random.choice(
                        ast.literal_eval(
                            self._config.get('BridgeDomainSettings',
                                             'UnknownMulticast'))))
            if self._config.get('BridgeDomains',
                                'AllowArpFlood').lower() == 'true':
                bd.set_arp_flood(
                    random.choice(
                        ast.literal_eval(
                            self._config.get('BridgeDomainSettings',
                                             'ArpFlood'))))
            if self._config.get('BridgeDomains',
                                'AllowDisableUnicastRoute').lower() == 'true':
                bd.set_unicast_route(
                    random.choice(
                        ast.literal_eval(
                            self._config.get('BridgeDomainSettings',
                                             'UnicastRoute'))))
            if self._config.get(
                    'BridgeDomains',
                    'AllowNonDefaultMultiDstPkt').lower() == 'true':
                bd.set_multidestination(
                    random.choice(
                        ast.literal_eval(
                            self._config.get('BridgeDomainSettings',
                                             'Multidestination'))))
            bridge_domains.append(bd)

        # Create some number of Contexts
        contexts = []
        max_contexts = int(self._config.get('Contexts', 'Maximum'))
        if max_contexts > self._global_limits.max_contexts:
            max_contexts = self._global_limits.max_contexts
        if max_contexts > int(self._config.get('Contexts',
                                               'MaximumPerTenant')):
            max_contexts = int(self._config.get('Contexts',
                                                'MaximumPerTenant'))
        for i in range(
                0,
                random_number(
                    0,
                    random_number(int(self._config.get('Contexts', 'Minimum')),
                                  max_contexts))):
            context = Context(
                random_string(random_number(1, max_string_length)), tenant)
            self._global_limits.max_contexts -= 1
            if self._config.get('Contexts',
                                'AllowUnenforced').lower() == 'true':
                context.set_allow_all(random.choice([True, False]))
            contexts.append(context)

        # Randomly associate BridgeDomains with the Contexts (or use default)
        for bd in bridge_domains:
            if random.choice([True, True, False]) and len(contexts):
                bd.add_context(random.choice(contexts))

        # Create some number of Application Profiles
        apps = []
        for i in range(
                0,
                random_number(
                    0,
                    random_number(
                        int(self._config.get('ApplicationProfiles',
                                             'Minimum')),
                        int(self._config.get('ApplicationProfiles',
                                             'Maximum'))))):
            app = AppProfile(
                random_string(random_number(1, max_string_length)), tenant)
            apps.append(app)

        # Create some number of EPGs and place in AppProfiles
        epgs = []
        max_epgs = int(self._config.get('EPGs', 'Maximum'))
        if max_epgs > self._global_limits.max_epgs:
            max_epgs = self._global_limits.max_epgs
        if len(apps):
            for i in range(
                    0,
                    random_number(
                        0,
                        random_number(int(self._config.get('EPGs', 'Minimum')),
                                      max_epgs))):
                epg = EPG(random_string(random_number(1, max_string_length)),
                          random.choice(apps))
                self._global_limits.max_epgs -= 1
                epgs.append(epg)

        # Randomly associate the EPGs to BridgeDomains
        bd_epg_count = [0] * len(bridge_domains)
        for epg in epgs:
            if random_number(0, 9) == 1 or len(
                    bridge_domains) == 0:  # 1 in 10 chance for no bridgedomain
                continue
            keep_trying = 100
            while keep_trying:
                bd_choice = random_number(0, len(bridge_domains) - 1)
                if bd_epg_count[bd_choice] <= int(
                        self._config.get('BridgeDomains', 'MaximumEPGs')):
                    epg.add_bd(bridge_domains[bd_choice])
                    bd_epg_count[bd_choice] += 1
                    break
                else:
                    keep_trying -= 1

        # Randomly assign the EPGs to the interfaces provided
        interface_objs = {}
        for interface in interfaces:
            # Create the Interface objects
            interface_objs[interface] = Interface.create_from_name(interface)
        for epg in epgs:
            # Pick an interface
            interface_choice = random.choice(interfaces)
            # Pick a VLAN
            vlan_choice = 0
            keep_trying = 100
            while vlan_choice in self._interfaces[interface_choice]:
                vlan_choice = random_number(
                    int(self._config.get('VLANs', 'Minimum')),
                    int(self._config.get('VLANs', 'Maximum')))
                keep_trying -= 1
            if not keep_trying:
                continue
            # Create the VLAN interface
            vlan_intf = L2Interface(
                'vlan%s-on-%s' %
                (str(vlan_choice),
                 interface_objs[interface_choice].name.replace(' ', '')),
                'vlan', str(vlan_choice))
            # Attach the VLAN interface to the Interface object
            vlan_intf.attach(interface_objs[interface_choice])
            # Attach the EPG to the VLAN interface
            epg.attach(vlan_intf)

        # Create some filters
        filters = []
        max_filters = int(self._config.get('Filters', 'Maximum'))
        if max_filters > self._global_limits.max_filters:
            max_filters = self._global_limits.max_filters
        for i in range(
                0,
                random_number(
                    0,
                    random_number(int(self._config.get('Filters', 'Minimum')),
                                  max_filters))):
            filter = Filter(random_string(random_number(1, max_string_length)),
                            tenant)
            self._global_limits.max_filters -= 1
            filters.append(filter)

        # Create some filter entries
        filter_entries = []
        max_filter_entries = int(self._config.get('FilterEntries', 'Maximum'))
        if max_filter_entries > self._global_limits.max_filter_entries:
            max_filter_entries = self._global_limits.max_filter_entries
        if len(filters):
            for i in range(
                    0,
                    random_number(
                        0,
                        random_number(
                            int(self._config.get('FilterEntries', 'Minimum')),
                            max_filter_entries))):
                applyToFrag = '0'
                arpOpc = '0'
                dFromPort = '0'
                dToPort = '0'
                prot = '0'
                sFromPort = '0'
                sToPort = '0'
                tcpRules = '0'
                stateful = '0'
                icmpv4T = 'not-given'
                icmpv6T = 'not-given'
                if random_chance(20):  # 20% chance of ARP
                    arpOpc = random.choice(
                        ast.literal_eval(
                            self._config.get('FilterEntryOptions', 'ARPCode')))
                    etherT = 'arp'
                elif random_chance(
                        25):  # 20% of remaining 80% is non-IP (16% of total)
                    ethertype_choices = ast.literal_eval(
                        self._config.get('Ethertypes', 'Choice16PC'))
                    # if not filter.has_wildcard_entry():
                    #     ethertype_choices += ['0']
                    etherT = random.choice(ethertype_choices)
                else:  # remaining is IP
                    applyToFrag = random.choice(
                        ast.literal_eval(
                            self._config.get('FilterEntryOptions',
                                             'Fragmentation')))
                    etherT = 'ip'
                    if random_chance(
                            20
                    ):  # Choose more obscure protocols 20% of the time
                        prot = ConfigRandomizer._ip_protocols[random.choice(
                            ast.literal_eval(
                                self._config.get('IPProtocols',
                                                 'Choice20PC')))]
                    else:
                        prot = ConfigRandomizer._ip_protocols[random.choice(
                            ast.literal_eval(
                                self._config.get('IPProtocols',
                                                 'Choice80PC')))]
                        if prot == ConfigRandomizer._ip_protocols['icmp']:
                            icmpv4T = random.choice(
                                ast.literal_eval(
                                    self._config.get('FilterEntryOptions',
                                                     'ICMP4Types')))
                            if icmpv4T != 'not-given':
                                # APIC will complain if both icmpv4T is specified and applyToFrag is set
                                applyToFrag = '0'
                        elif prot == ConfigRandomizer._ip_protocols['icmpv6']:
                            icmpv6T = random.choice(
                                ast.literal_eval(
                                    self._config.get('FilterEntryOptions',
                                                     'ICMP6Types')))
                            if icmpv6T != 'not-given':
                                # APIC will complain if both icmpv6T is specified and applyToFrag is set
                                applyToFrag = '0'
                        else:
                            # Remainder is TCP or UDP
                            dFromPort, dToPort = random_range(
                                int(
                                    self._config.get('FilterEntryOptions',
                                                     'PortRangeMin')),
                                int(
                                    self._config.get('FilterEntryOptions',
                                                     'PortRangeMax')))
                            sFromPort, sToPort = random_range(
                                int(
                                    self._config.get('FilterEntryOptions',
                                                     'PortRangeMin')),
                                int(
                                    self._config.get('FilterEntryOptions',
                                                     'PortRangeMax')))
                            if dFromPort != '0' or dToPort != '0' or sFromPort != '0' or sToPort != '0':
                                applyToFrag = '0'
                            if prot == ConfigRandomizer._ip_protocols['tcp']:
                                # Randomly choose whether to specify tcpRules
                                if random_chance(30):
                                    # TODO: should actually take odds from the config file
                                    # Choose a random number of the possible tcpRules but
                                    # if est is chosen, then it must be the only tcpRule. Otherwise, APIC rejects it
                                    tcp_rule_choices = []
                                    tcp_rule_possibilities = ast.literal_eval(
                                        self._config.get(
                                            'FilterEntryOptions', 'TCPRules'))
                                    tcp_choice = random.choice(
                                        tcp_rule_possibilities)
                                    tcp_rule_choices.append(tcp_choice)
                                    while tcp_choice != 'est':
                                        tcp_choice = random.choice(
                                            tcp_rule_possibilities)
                                        if tcp_choice != 'est' and tcp_choice not in tcp_rule_choices:
                                            tcp_rule_choices.append(tcp_choice)
                                    tcpRules = ''
                                    for tcp_choice in tcp_rule_choices:
                                        tcpRules += str(tcp_choice) + ','
                parent = random.choice(filters)
                if not parent.has_entry(applyToFrag, arpOpc, dFromPort,
                                        dToPort, etherT, prot, sFromPort,
                                        sToPort, tcpRules, stateful, icmpv4T,
                                        icmpv6T):
                    filter_entry = FilterEntry(name=random_string(
                        random_number(1, max_string_length)),
                                               parent=parent,
                                               applyToFrag=applyToFrag,
                                               arpOpc=arpOpc,
                                               dFromPort=dFromPort,
                                               dToPort=dToPort,
                                               etherT=etherT,
                                               prot=prot,
                                               sFromPort=sFromPort,
                                               sToPort=sToPort,
                                               tcpRules=tcpRules,
                                               stateful=stateful,
                                               icmpv4T=icmpv4T,
                                               icmpv6T=icmpv6T)
                    # for l2tp traffic type we also need to udp filter with src and dst ports 1701
                    if etherT == 'ip' and prot == ConfigRandomizer._ip_protocols[
                            'l2tp']:
                        filter_entry = FilterEntry(
                            name=random_string(
                                random_number(1, max_string_length)),
                            parent=parent,
                            applyToFrag='0',
                            arpOpc=arpOpc,
                            dFromPort='1701',
                            dToPort='1701',
                            etherT=etherT,
                            prot=ConfigRandomizer._ip_protocols['udp'],
                            sFromPort='1701',
                            sToPort='1701',
                            tcpRules=tcpRules,
                            stateful=stateful,
                            icmpv4T=icmpv4T,
                            icmpv6T=icmpv6T)
                self._global_limits.max_filter_entries -= 1

        # Create some Contracts
        contracts = []
        max_contracts = int(self._config.get('Contracts', 'Maximum'))
        if max_contracts > self._global_limits.max_contracts:
            max_contracts = self._global_limits.max_contracts
        for i in range(
                0,
                random_number(
                    0,
                    random_number(
                        int(self._config.get('Contracts', 'Minimum')),
                        max_contracts))):
            contract = Contract(
                random_string(random_number(1, max_string_length)), tenant)
            self._global_limits.max_contracts -= 1
            contracts.append(contract)

        # Create some ContractSubjects
        contract_subjects = []
        if len(contracts):
            for i in range(
                    0,
                    random_number(
                        0,
                        random_number(
                            int(self._config.get('ContractSubjects',
                                                 'Minimum')),
                            int(self._config.get('ContractSubjects',
                                                 'Maximum'))))):
                contract_subject = ContractSubject(
                    random_string(random_number(1, max_string_length)),
                    random.choice(contracts))
                contract_subjects.append(contract_subject)

        # Randomly assign Filters to the ContractSubjects
        for filter in filters:
            if len(contracts) and len(contract_subjects):
                already_picked = []
                # Pick an arbitrary number of Subjects
                for i in range(0, random_number(1, len(contract_subjects))):
                    pick = random_number(0, len(contract_subjects) - 1)
                    # Only choose each subject at most once
                    if pick not in already_picked:
                        contract_subjects[pick].add_filter(filter)
                        already_picked.append(pick)

        # Randomly provide and consume the Contracts from the EPGs
        for action, max_num_epgs in [
            ('provide',
             int(self._config.get('Contracts', 'MaximumProvidingEPGs'))),
            ('consume',
             int(self._config.get('Contracts', 'MaximumConsumingEPGs')))
        ]:
            contract_count = [0] * len(contracts)
            for epg in epgs:
                already_picked = []
                for i in range(0, random_number(0, len(contracts))):
                    keep_trying = 20  # Try 20 times to randomly pick a contract
                    while keep_trying:
                        pick = random_number(0, len(contracts) - 1)
                        if pick not in already_picked and contract_count[
                                pick] < max_num_epgs:
                            getattr(epg, action)(contracts[pick])
                            already_picked.append(pick)
                            contract_count[pick] += 1
                            keep_trying = 0
                        else:
                            keep_trying -= 1

        # Create some Taboos
        taboos = []
        for i in range(
                0,
                random_number(
                    0,
                    random_number(int(self._config.get('Taboos', 'Minimum')),
                                  int(self._config.get('Taboos',
                                                       'Maximum'))))):
            taboo = Taboo(random_string(random_number(1, max_string_length)),
                          tenant)
            taboos.append(taboo)

        # Create some Taboo ContractSubjects
        taboo_contract_subjects = []
        if len(taboos):
            for i in range(
                    0,
                    random_number(
                        1,
                        random_number(
                            int(
                                self._config.get('TabooContractSubjects',
                                                 'Minimum')),
                            int(
                                self._config.get('TabooContractSubjects',
                                                 'Maximum'))))):
                taboo_contract_subject = ContractSubject(
                    random_string(random_number(1, max_string_length)),
                    random.choice(taboos))
                taboo_contract_subjects.append(taboo_contract_subject)

        # Randomly assign Filters to TabooContractSubjects
        for filter in filters:
            if len(taboo_contract_subjects):
                already_picked = []
                # Pick an arbitrary number of Subjects
                for i in range(0, random_number(1,
                                                len(taboo_contract_subjects))):
                    pick = random_number(0, len(taboo_contract_subjects) - 1)
                    # Only choose each subject at most once
                    if pick not in already_picked:
                        taboo_contract_subjects[pick].add_filter(filter)
                        already_picked.append(pick)

        # Randomly protect epgs with taboos
        for epg in epgs:
            if random.choice([True, False, True]) and len(taboos):
                epg.protect(taboos[random_number(0, len(taboos) - 1)])

        return tenant
    def create_random_tenant(self, interfaces=[]):
        max_string_length = int(self._config.get('GlobalDefaults', 'MaximumStringLength'))
        # Create the Tenant object
        tenant_prefix = self._config.get('GlobalDefaults', 'TenantPrefix')
        tenant_name_len = random_number(1, max_string_length - len(tenant_prefix))
        tenant_name = tenant_prefix + random_string(tenant_name_len)
        tenant = Tenant(tenant_name)

        # Create some number of BridgeDomains
        bridge_domains = []
        maximum_bds = int(self._config.get('BridgeDomains', 'Maximum'))
        if maximum_bds > self._global_limits.max_bds:
            maximum_bds = self._global_limits.max_bds
        for i in range(0, random_number(0, random_number(int(self._config.get('BridgeDomains', 'Minimum')),
                                                         maximum_bds))):
            self._global_limits.max_bds -= 1
            bd = BridgeDomain(random_string(random_number(1, max_string_length)), tenant)
            # Randomly choose settings for the BridgeDomain
            if self._config.get('BridgeDomains', 'AllowFloodUnkMacUcast').lower() == 'true':
                bd.set_unknown_mac_unicast(random.choice(ast.literal_eval(self._config.get('BridgeDomainSettings', 'UnknownMacUnicast'))))
            if self._config.get('BridgeDomains', 'AllowOptimizedFloodUnknownMcast').lower() == 'true':
                bd.set_unknown_multicast(random.choice(ast.literal_eval(self._config.get('BridgeDomainSettings', 'UnknownMulticast'))))
            if self._config.get('BridgeDomains', 'AllowArpFlood').lower() == 'true':
                bd.set_arp_flood(random.choice(ast.literal_eval(self._config.get('BridgeDomainSettings', 'ArpFlood'))))
            if self._config.get('BridgeDomains', 'AllowDisableUnicastRoute').lower() == 'true':
                bd.set_unicast_route(random.choice(ast.literal_eval(self._config.get('BridgeDomainSettings', 'UnicastRoute'))))
            if self._config.get('BridgeDomains', 'AllowNonDefaultMultiDstPkt').lower() == 'true':
                bd.set_multidestination(random.choice(ast.literal_eval(self._config.get('BridgeDomainSettings', 'Multidestination'))))
            bridge_domains.append(bd)

        # Create some number of Contexts
        contexts = []
        max_contexts = int(self._config.get('Contexts', 'Maximum'))
        if max_contexts > self._global_limits.max_contexts:
            max_contexts = self._global_limits.max_contexts
        if max_contexts > int(self._config.get('Contexts', 'MaximumPerTenant')):
            max_contexts = int(self._config.get('Contexts', 'MaximumPerTenant'))
        for i in range(0, random_number(0, random_number(int(self._config.get('Contexts', 'Minimum')),
                                                         max_contexts))):
            context = Context(random_string(random_number(1, max_string_length)), tenant)
            self._global_limits.max_contexts -= 1
            if self._config.get('Contexts', 'AllowUnenforced').lower() == 'true':
                context.set_allow_all(random.choice([True, False]))
            contexts.append(context)

        # Randomly associate BridgeDomains with the Contexts (or use default)
        for bd in bridge_domains:
            if random.choice([True, True, False]) and len(contexts):
                bd.add_context(random.choice(contexts))

        # Create some number of Application Profiles
        apps = []
        for i in range(0, random_number(0, random_number(int(self._config.get('ApplicationProfiles', 'Minimum')),
                                                         int(self._config.get('ApplicationProfiles', 'Maximum'))))):
            app = AppProfile(random_string(random_number(1, max_string_length)), tenant)
            apps.append(app)

        # Create some number of EPGs and place in AppProfiles
        epgs = []
        max_epgs = int(self._config.get('EPGs', 'Maximum'))
        if max_epgs > self._global_limits.max_epgs:
            max_epgs = self._global_limits.max_epgs
        if len(apps):
            for i in range(0, random_number(0, random_number(int(self._config.get('EPGs', 'Minimum')),
                                                             max_epgs))):
                epg = EPG(random_string(random_number(1, max_string_length)), random.choice(apps))
                self._global_limits.max_epgs -= 1
                epgs.append(epg)

        # Randomly associate the EPGs to BridgeDomains
        bd_epg_count = [0] * len(bridge_domains)
        for epg in epgs:
            if random_number(0, 9) == 1 or len(bridge_domains) == 0:   # 1 in 10 chance for no bridgedomain
                continue
            keep_trying = 100
            while keep_trying:
                bd_choice = random_number(0, len(bridge_domains) - 1)
                if bd_epg_count[bd_choice] <= int(self._config.get('BridgeDomains', 'MaximumEPGs')):
                    epg.add_bd(bridge_domains[bd_choice])
                    bd_epg_count[bd_choice] += 1
                    break
                else:
                    keep_trying -= 1

        # Randomly assign the EPGs to the interfaces provided
        interface_objs = {}
        for interface in interfaces:
            # Create the Interface objects
            interface_objs[interface] = Interface.create_from_name(interface)
        for epg in epgs:
            # Pick an interface
            interface_choice = random.choice(interfaces)
            # Pick a VLAN
            vlan_choice = 0
            keep_trying = 100
            while vlan_choice in self._interfaces[interface_choice]:
                vlan_choice = random_number(int(self._config.get('VLANs', 'Minimum')),
                                            int(self._config.get('VLANs', 'Maximum')))
                keep_trying -= 1
            if not keep_trying:
                continue
            # Create the VLAN interface
            vlan_intf = L2Interface('vlan%s-on-%s' % (str(vlan_choice),
                                                      interface_objs[interface_choice].name.replace(' ', '')),
                                    'vlan',
                                    str(vlan_choice))
            # Attach the VLAN interface to the Interface object
            vlan_intf.attach(interface_objs[interface_choice])
            # Attach the EPG to the VLAN interface
            epg.attach(vlan_intf)

        # Create some filters
        filters = []
        max_filters = int(self._config.get('Filters', 'Maximum'))
        if max_filters > self._global_limits.max_filters:
            max_filters = self._global_limits.max_filters
        for i in range(0, random_number(0, random_number(int(self._config.get('Filters', 'Minimum')),
                                                         max_filters))):
            filter = Filter(random_string(random_number(1, max_string_length)), tenant)
            self._global_limits.max_filters -= 1
            filters.append(filter)

        # Create some filter entries
        filter_entries = []
        max_filter_entries = int(self._config.get('FilterEntries', 'Maximum'))
        if max_filter_entries > self._global_limits.max_filter_entries:
            max_filter_entries = self._global_limits.max_filter_entries
        if len(filters):
            for i in range(0, random_number(0, random_number(int(self._config.get('FilterEntries', 'Minimum')),
                                                             max_filter_entries))):
                applyToFrag = '0'
                arpOpc = '0'
                dFromPort = '0'
                dToPort = '0'
                prot = '0'
                sFromPort = '0'
                sToPort = '0'
                tcpRules = '0'
                stateful = '0'
                icmpv4T = 'not-given'
                icmpv6T = 'not-given'
                if random_chance(20):  # 20% chance of ARP
                    arpOpc = random.choice(ast.literal_eval(self._config.get('FilterEntryOptions', 'ARPCode')))
                    etherT = 'arp'
                elif random_chance(25):  # 20% of remaining 80% is non-IP (16% of total)
                    ethertype_choices = ast.literal_eval(self._config.get('Ethertypes', 'Choice16PC'))
                    # if not filter.has_wildcard_entry():
                    #     ethertype_choices += ['0']
                    etherT = random.choice(ethertype_choices)
                else:  # remaining is IP
                    applyToFrag = random.choice(ast.literal_eval(self._config.get('FilterEntryOptions', 'Fragmentation')))
                    etherT = 'ip'
                    if random_chance(20):  # Choose more obscure protocols 20% of the time
                        prot = ConfigRandomizer._ip_protocols[random.choice(ast.literal_eval(self._config.get('IPProtocols', 'Choice20PC')))]
                    else:
                        prot = ConfigRandomizer._ip_protocols[random.choice(ast.literal_eval(self._config.get('IPProtocols', 'Choice80PC')))]
                        if prot == ConfigRandomizer._ip_protocols['icmp']:
                            icmpv4T = random.choice(ast.literal_eval(self._config.get('FilterEntryOptions', 'ICMP4Types')))
                        elif prot == ConfigRandomizer._ip_protocols['icmpv6']:
                            icmpv6T = random.choice(ast.literal_eval(self._config.get('FilterEntryOptions', 'ICMP6Types')))
                        else:
                            # Remainder is TCP or UDP
                            dFromPort, dToPort = random_range(int(self._config.get('FilterEntryOptions', 'PortRangeMin')), int(self._config.get('FilterEntryOptions', 'PortRangeMax')))
                            sFromPort, sToPort = random_range(int(self._config.get('FilterEntryOptions', 'PortRangeMin')), int(self._config.get('FilterEntryOptions', 'PortRangeMax')))
                            if dFromPort != '0' or dToPort != '0' or sFromPort != '0' or sToPort != '0':
                                applyToFrag = '0'
                            if prot == ConfigRandomizer._ip_protocols['tcp']:
                                # Randomly choose whether to specify tcpRules
                                if random_chance(30):
                                    # TODO: should actually take odds from the config file
                                    # Choose a random number of the possible tcpRules but
                                    # if est is chosen, then it must be the only tcpRule. Otherwise, APIC rejects it
                                    tcp_rule_choices = []
                                    tcp_rule_possibilities = ast.literal_eval(self._config.get('FilterEntryOptions', 'TCPRules'))
                                    tcp_choice = random.choice(tcp_rule_possibilities)
                                    tcp_rule_choices.append(tcp_choice)
                                    while tcp_choice != 'est':
                                        tcp_choice = random.choice(tcp_rule_possibilities)
                                        if tcp_choice != 'est' and tcp_choice not in tcp_rule_choices:
                                            tcp_rule_choices.append(tcp_choice)
                                    tcpRules = ''
                                    for tcp_choice in tcp_rule_choices:
                                        tcpRules += str(tcp_choice) + ','
                parent = random.choice(filters)
                if not parent.has_entry(applyToFrag, arpOpc, dFromPort, dToPort, etherT, prot, sFromPort, sToPort,
                                        tcpRules, stateful, icmpv4T, icmpv6T):
                    filter_entry = FilterEntry(name=random_string(random_number(1, max_string_length)),
                                               parent=parent,
                                               applyToFrag=applyToFrag,
                                               arpOpc=arpOpc,
                                               dFromPort=dFromPort,
                                               dToPort=dToPort,
                                               etherT=etherT,
                                               prot=prot,
                                               sFromPort=sFromPort,
                                               sToPort=sToPort,
                                               tcpRules=tcpRules,
                                               stateful=stateful,
                                               icmpv4T=icmpv4T,
                                               icmpv6T=icmpv6T)
                    #for l2tp traffic type we also need to udp filter with src and dst ports 1701
                    if etherT == 'ip' and prot == ConfigRandomizer._ip_protocols['l2tp']:
                        filter_entry = FilterEntry(name=random_string(random_number(1, max_string_length)),
                                               parent=parent,
                                               applyToFrag='0',
                                               arpOpc=arpOpc,
                                               dFromPort='1701',
                                               dToPort='1701',
                                               etherT=etherT,
                                               prot=ConfigRandomizer._ip_protocols['udp'],
                                               sFromPort='1701',
                                               sToPort='1701',
                                               tcpRules=tcpRules,
                                               stateful=stateful,
                                               icmpv4T=icmpv4T,
                                               icmpv6T=icmpv6T)
                self._global_limits.max_filter_entries -= 1

        # Create some Contracts
        contracts = []
        max_contracts = int(self._config.get('Contracts', 'Maximum'))
        if max_contracts > self._global_limits.max_contracts:
            max_contracts = self._global_limits.max_contracts
        for i in range(0, random_number(0, random_number(int(self._config.get('Contracts', 'Minimum')),
                                                         max_contracts))):
            contract = Contract(random_string(random_number(1, max_string_length)), tenant)
            self._global_limits.max_contracts -= 1
            contracts.append(contract)

        # Create some ContractSubjects
        contract_subjects = []
        if len(contracts):
            for i in range(0, random_number(0, random_number(int(self._config.get('ContractSubjects', 'Minimum')),
                                                             int(self._config.get('ContractSubjects', 'Maximum'))))):
                contract_subject = ContractSubject(random_string(random_number(1, max_string_length)), random.choice(contracts))
                contract_subjects.append(contract_subject)

        # Randomly assign Filters to the ContractSubjects
        for filter in filters:
            if len(contracts) and len(contract_subjects):
                already_picked = []
                # Pick an arbitrary number of Subjects
                for i in range(0, random_number(1, len(contract_subjects))):
                    pick = random_number(0, len(contract_subjects) - 1)
                    # Only choose each subject at most once
                    if pick not in already_picked:
                        contract_subjects[pick].add_filter(filter)
                        already_picked.append(pick)

        # Randomly provide and consume the Contracts from the EPGs
        for action, max_num_epgs in [('provide', int(self._config.get('Contracts', 'MaximumProvidingEPGs'))),
                                     ('consume', int(self._config.get('Contracts', 'MaximumConsumingEPGs')))]:
            contract_count = [0] * len(contracts)
            for epg in epgs:
                already_picked = []
                for i in range(0, random_number(0, len(contracts))):
                    keep_trying = 20  # Try 20 times to randomly pick a contract
                    while keep_trying:
                        pick = random_number(0, len(contracts) - 1)
                        if pick not in already_picked and contract_count[pick] < max_num_epgs:
                            getattr(epg, action)(contracts[pick])
                            already_picked.append(pick)
                            contract_count[pick] += 1
                            keep_trying = 0
                        else:
                            keep_trying -= 1
        
        # Create some Taboos    
        taboos = []
        for i in range(0, random_number(0, random_number(int(self._config.get('Taboos', 'Minimum')),
                                                         int(self._config.get('Taboos', 'Maximum'))))):
            taboo = Taboo(random_string(random_number(1, max_string_length)), tenant)
            taboos.append(taboo)
            
        # Create some Taboo ContractSubjects
        taboo_contract_subjects = []
        if len(taboos):
            for i in range(0, random_number(1, random_number(int(self._config.get('TabooContractSubjects', 'Minimum')),
                                                             int(self._config.get('TabooContractSubjects', 'Maximum'))))):
                taboo_contract_subject = ContractSubject(random_string(random_number(1, max_string_length)), random.choice(taboos))
                taboo_contract_subjects.append(taboo_contract_subject)
                
        # Randomly assign Filters to TabooContractSubjects
        for filter in filters:
            if len(taboo_contract_subjects):
                already_picked = []
                # Pick an arbitrary number of Subjects
                for i in range(0, random_number(1, len(taboo_contract_subjects))):
                    pick = random_number(0, len(taboo_contract_subjects) - 1)
                    # Only choose each subject at most once
                    if pick not in already_picked:
                        taboo_contract_subjects[pick].add_filter(filter)
                        already_picked.append(pick)
        
        # Randomly protect epgs with taboos                
        for epg in epgs:
            if random.choice([True, False, True]) and len(taboos):
                epg.protect(taboos[random_number(0, len(taboos)-1)])
        
        return tenant