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