예제 #1
0
 def set_up(self, n_dps, host_links=None, host_vlans=None):
     super(FaucetSingleUntaggedIPV4RoutingWithStackingTest, self).setUp()
     n_vlans = 3
     routed_vlans = 2
     stack_roots = {0: 1}
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.path_graph(n_dps))
     if not host_links and not host_vlans:
         host_links, host_vlans = FaucetTopoGenerator.untagged_vlan_hosts(
             n_dps, routed_vlans)
     vlan_options = {}
     for v in range(routed_vlans):
         vlan_options[v] = {
             'faucet_mac': self.faucet_mac(v),
             'faucet_vips': [self.faucet_vip(v)],
             'targeted_gw_resolution': False
         }
     dp_options = {dp: self.get_dp_options() for dp in range(n_dps)}
     routers = {0: [v for v in range(routed_vlans)]}
     self.build_net(n_dps=n_dps,
                    n_vlans=n_vlans,
                    dp_links=dp_links,
                    host_links=host_links,
                    host_vlans=host_vlans,
                    stack_roots=stack_roots,
                    vlan_options=vlan_options,
                    dp_options=dp_options,
                    routers=routers)
     self.start_net()
예제 #2
0
 def set_up(self,
            stack=False,
            n_dps=1,
            n_tagged=0,
            n_untagged=0,
            include=None,
            include_optional=None,
            switch_to_switch_links=1,
            hw_dpid=None,
            stack_ring=False,
            lacp=False,
            use_external=False,
            vlan_options=None,
            dp_options=None,
            routers=None):
     """Set up a network with the given parameters"""
     super(FaucetMultiDPTest, self).setUp()
     n_vlans = 1
     dp_links = {}
     if stack_ring:
         dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
             networkx.cycle_graph(n_dps), n_dp_links=switch_to_switch_links)
     else:
         dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
             networkx.path_graph(n_dps), n_dp_links=switch_to_switch_links)
     stack_roots = None
     if stack:
         stack_roots = {0: 1}
     host_links, host_vlans = FaucetTopoGenerator.tagged_untagged_hosts(
         n_dps, n_tagged, n_untagged)
     host_options = {}
     values = [False for _ in range(n_dps)]
     if use_external:
         for host_id, links in host_links.items():
             for link in links:
                 host_options[host_id] = {
                     'loop_protect_external': values[link]
                 }
                 values[link] = True
     self.build_net(n_dps=n_dps,
                    n_vlans=n_vlans,
                    dp_links=dp_links,
                    host_links=host_links,
                    host_vlans=host_vlans,
                    stack_roots=stack_roots,
                    vlan_options=vlan_options,
                    dp_options=dp_options,
                    routers=routers,
                    include=include,
                    include_optional=include_optional,
                    hw_dpid=hw_dpid,
                    lacp=lacp,
                    host_options=host_options)
     self.start_net()
예제 #3
0
 def test_3_ladder(self):
     """Test fault-tolerance of a complete ladder graph n=3"""
     stack_roots = {i: 1 for i in range(self.NUM_DPS // 2)}
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.ladder_graph(self.NUM_DPS // 2))
     self.set_up(self.NUM_DPS, self.NUM_VLANS, dp_links, stack_roots)
     self.network_function()
예제 #4
0
 def test_fat_tree_3(self):
     """Test fault-tolerance of a 6-cycle/3-fat tree pod"""
     stack_roots = {i: 1 for i in range(self.NUM_DPS // 2)}
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.cycle_graph(self.NUM_DPS))
     self.set_up(self.NUM_DPS, self.NUM_VLANS, dp_links, stack_roots)
     self.network_function()
예제 #5
0
 def test_k23(self):
     """Test fault-tolerance of a complete bipartite graph K_{2,3}"""
     stack_roots = {i: 1 for i in range(2)}
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.complete_bipartite_graph(2, 3))
     self.set_up(self.NUM_DPS, self.NUM_VLANS, dp_links, stack_roots)
     self.network_function()
예제 #6
0
 def set_up(self):
     super(FaucetSingleUntaggedVlanStackFloodTest, self).setUp()
     stack_roots = {0: 1}
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.path_graph(self.NUM_DPS))
     host_links = {0: [0], 1: [1]}
     host_vlans = {0: 0, 1: 1}
     vlan_options = {}
     for v in range(self.NUM_VLANS):
         vlan_options[v] = {
             'faucet_mac': self.faucet_mac(v),
             'faucet_vips': [self.faucet_vip(v)],
             'targeted_gw_resolution': False
         }
     dp_options = {dp: self.get_dp_options() for dp in range(self.NUM_DPS)}
     routers = {0: [v for v in range(self.NUM_VLANS)]}
     self.build_net(n_dps=self.NUM_DPS,
                    n_vlans=self.NUM_VLANS,
                    dp_links=dp_links,
                    host_links=host_links,
                    host_vlans=host_vlans,
                    stack_roots=stack_roots,
                    vlan_options=vlan_options,
                    dp_options=dp_options,
                    routers=routers)
     self.start_net()
예제 #7
0
 def test_3_line(self):
     """Test fault-tolerance of a path of length 3"""
     stack_roots = {0: 1}
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.path_graph(self.NUM_DPS))
     self.set_up(self.NUM_DPS, self.NUM_VLANS, dp_links, stack_roots)
     self.network_function()
예제 #8
0
 def test_4_node_ring_links(self):
     """Test fault-tolerance of a 4-cycle graph"""
     stack_roots = {0: 1}
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.cycle_graph(self.NUM_DPS))
     self.set_up(self.NUM_DPS, self.NUM_VLANS, dp_links, stack_roots)
     self.network_function()
예제 #9
0
 def test_ftp2_edge_root_link_fault(self):
     """Test breaking a link between a edge switch to the root aggregation switch"""
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.cycle_graph(self.NUM_DPS))
     fault_events = [(self.dp_link_fault, (0, 3))]
     stack_roots = {2 * i: i + 1 for i in range(self.NUM_DPS // 2)}
     self.set_up(self.NUM_DPS, self.NUM_VLANS, dp_links, stack_roots)
     self.network_function(fault_events=fault_events)
예제 #10
0
 def test_ftp2_all_random_link_failures(self):
     """Test fat-tree-pod-2 randomly tearing down only switch-switch links"""
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.cycle_graph(self.NUM_DPS))
     fault_events = [(self.random_dp_link_fault, (None, ))
                     for _ in range(len(dp_links))]
     stack_roots = {2 * i: 1 for i in range(self.NUM_DPS // 2)}
     self.set_up(self.NUM_DPS, self.NUM_VLANS, dp_links, stack_roots)
     self.network_function(fault_events=fault_events)
예제 #11
0
 def test_ftp2_destroying_one_of_each_link(self):
     """Test tearing down one of each link for a fat-tree-pod-2 with redundant edges"""
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.cycle_graph(self.NUM_DPS), n_dp_links=2)
     fault_events = []
     for i in range(self.NUM_DPS):
         j = i + 1 if i + 1 < self.NUM_DPS else 0
         fault_events.append((self.dp_link_fault, (i, j)))
     num_faults = len(fault_events)
     stack_roots = {2 * i: 1 for i in range(self.NUM_DPS // 2)}
     self.set_up(self.NUM_DPS, self.NUM_VLANS, dp_links, stack_roots)
     self.network_function(fault_events=fault_events, num_faults=num_faults)
예제 #12
0
 def set_up(self,
            n_dps,
            n_vlans,
            dp_links,
            stack_roots,
            host_links=None,
            host_vlans=None,
            host_options=None):
     """
     Args:
         n_dps: Number of DPS to generate
         n_vlans: Number of VLANs to generate
         dp_links (dict): Topology to deploy
         stack_roots (dict): Stack root values for respective stack root DPS
         host_links (dict): (optional)
         host_vlans (dict): (optional)
         host_options (dict): (optional)
     """
     super(FaucetFaultToleranceBaseTest, self).setUp()
     if not host_links or not host_vlans:
         host_links, host_vlans = FaucetTopoGenerator.untagged_vlan_hosts(
             n_dps, n_vlans)
     vlan_options = {}
     if n_vlans >= 2:
         for i in range(n_vlans):
             vlan_options[i] = {
                 'faucet_mac': self.faucet_mac(i),
                 'faucet_vips': [self.faucet_vip(i)],
                 'targeted_gw_resolution': False
             }
     dp_options = {}
     if n_vlans >= 2:
         for i in range(n_dps):
             dp_options[i] = {
                 'drop_spoofed_faucet_mac': False,
                 'arp_neighbor_timeout': 2,
                 'max_resolve_backoff_time': 2,
                 'proactive_learn_v4': True
             }
     routers = {}
     if n_vlans >= 2:
         routers = {0: list(range(n_vlans))}
     self.build_net(n_dps=n_dps,
                    n_vlans=n_vlans,
                    dp_links=dp_links,
                    host_links=host_links,
                    host_vlans=host_vlans,
                    stack_roots=stack_roots,
                    vlan_options=vlan_options,
                    dp_options=dp_options,
                    routers=routers,
                    host_options=host_options)
     self.start_net()
예제 #13
0
파일: test_topo.py 프로젝트: mubix/faucet
    def test_hw_remap(self):
        """Test remapping of attachment bridge port numbers to hw port numbers"""
        # Create a basic string topo
        peer_link = FaucetTopoGenerator.peer_link
        switch_map = {1:'p1', 2:'p2', 3:'p3', 4:'p4', 5:'p5', 6:'p6'}
        n_dps = len(self.dpids)
        n_tagged = 2
        n_untagged = 2
        host_links, host_vlans = FaucetTopoGenerator.tagged_untagged_hosts(
            n_dps, n_tagged, n_untagged)
        dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
            networkx.path_graph(n_dps), n_dp_links=2)
        args = self.string_of_dp_args(
            dp_links=dp_links,
            host_links=host_links,
            host_vlans=host_vlans,
            vlan_vids=self.vlan_vids,
            start_port=5,
            hw_dpid='1',
            switch_map=switch_map)
        topo = FaucetTopoGenerator(**args)

        # Verify switch ports
        switch_ports = {dpid: topo.dpid_ports(dpid) for dpid in self.dpids}

        self.assertEqual(
            switch_ports,
            # 4 host ports and 2/4/2 peer links, respectively
            {
                # "Hardware" switch should start at 1
                '1': [1, 2, 3, 4, 5, 6],
                # Software switches start at start_port
                '2': [5, 6, 7, 8, 9, 10, 11, 12],
                '3': [5, 6, 7, 8, 9, 10]
            },
            "switch ports are incorrect")

        # Verify peer links
        peer_links = {dpid: topo.dpid_peer_links(dpid) for dpid in self.dpids}

        self.assertEqual(
            peer_links,
            # Should be linked to previous and next switch
            {
                '1': [
                    peer_link(port=5, peer_dpid='2', peer_port=9),
                    peer_link(port=6, peer_dpid='2', peer_port=10)
                ],
                '2': [
                    peer_link(port=9, peer_dpid='1', peer_port=5),
                    peer_link(port=10, peer_dpid='1', peer_port=6),
                    peer_link(port=11, peer_dpid='3', peer_port=9),
                    peer_link(port=12, peer_dpid='3', peer_port=10)
                ],
                '3': [
                    peer_link(port=9, peer_dpid='2', peer_port=11),
                    peer_link(port=10, peer_dpid='2', peer_port=12)
                ]
            },
            "peer links are incorrect")
예제 #14
0
파일: test_topo.py 프로젝트: mubix/faucet
    def test_string_of_dp_sanity(self):
        """FaucetTopoGenerator sanity test"""

        # Create a basic string topo
        n_dps = len(self.dpids)
        n_tagged = 2
        n_untagged = 2
        peer_link = FaucetTopoGenerator.peer_link
        host_links, host_vlans = FaucetTopoGenerator.tagged_untagged_hosts(
            n_dps, n_tagged, n_untagged)
        dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
            networkx.path_graph(n_dps), n_dp_links=2)
        args = self.string_of_dp_args(
            dp_links=dp_links,
            host_links=host_links,
            host_vlans=host_vlans,
            vlan_vids=self.vlan_vids,
            start_port=1)
        topo = FaucetTopoGenerator(**args)

        # Verify switch ports
        ports = {dpid: topo.dpid_ports(dpid) for dpid in self.dpids}

        self.assertEqual(
            ports,
            # 4 host ports and 2/4/2 peer links, respectively
            {
                '1': [1, 2, 3, 4, 5, 6],
                '2': [1, 2, 3, 4, 5, 6, 7, 8],
                '3': [1, 2, 3, 4, 5, 6]
            },
            "switch ports are incorrect")

        # Verify peer links
        peer_links = {dpid: topo.dpid_peer_links(dpid) for dpid in self.dpids}

        self.assertEqual(
            peer_links,
            # Should be linked to previous and next switch
            {
                '1': [
                    peer_link(port=5, peer_dpid='2', peer_port=5),
                    peer_link(port=6, peer_dpid='2', peer_port=6)
                ],
                '2': [
                    peer_link(port=5, peer_dpid='1', peer_port=5),
                    peer_link(port=6, peer_dpid='1', peer_port=6),
                    peer_link(port=7, peer_dpid='3', peer_port=5),
                    peer_link(port=8, peer_dpid='3', peer_port=6)
                ],
                '3': [
                    peer_link(port=5, peer_dpid='2', peer_port=7),
                    peer_link(port=6, peer_dpid='2', peer_port=8)
                ]
            },
            "peer links are incorrect")
예제 #15
0
 def test_ftp2_external_host(self):
     """Test fat-tree-pod-2 with an external host connected to both roots"""
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         networkx.cycle_graph(self.NUM_DPS))
     stack_roots = {2 * i: 1 for i in range(self.NUM_DPS // 2)}
     host_links = {0: [0, 2], 1: [1], 2: [3]}
     host_vlans = {0: 0, 1: 0, 2: 0}
     host_options = {0: {'loop_protect_external': True}}
     self.set_up(self.NUM_DPS,
                 self.NUM_VLANS,
                 dp_links,
                 stack_roots,
                 host_links=host_links,
                 host_vlans=host_vlans,
                 host_options=host_options)
     self.network_function()
예제 #16
0
class FaucetTopoTestBase(FaucetTestBase):
    """
    Extension to the base test for the integration test suite to help set up arbitrary topologies
    This is supposed to be called with clib.mininet_topo_generator to take networkx graph
        generators and produce a set of switch-switch links and host-switch links
    """

    NETPREFIX = 24
    IPV = 4
    GROUP_TABLE = False

    NUM_DPS = 2
    NUM_HOSTS = 4
    LINKS_PER_HOST = 1

    dpids = None
    port_maps = None
    n_vlans = 0
    dp_links = None
    host_links = None
    host_vlans = None
    stack_roots = None
    routers = None
    dp_options = None
    host_options = None
    vlan_options = None
    host_information = None
    faucet_vips = None

    def non_host_links(self, dpid):
        """Return dpid peer links from topo"""
        return self.topo.dpid_peer_links(dpid)

    @staticmethod
    def get_config_header(_config_global, _debug_log, _dpid, _hardware):
        """Don't generate standard config file header."""
        return ''

    @staticmethod
    def acls():
        """Dictionary of ACLs"""
        return {}

    @staticmethod
    def acl_in_dp():
        """Dictionary of DP & port map to ACL"""
        return {}

    @staticmethod
    def dp_name(i):
        """DP config name"""
        return 'faucet-%u' % (i + 1)

    def faucet_vip(self, i):
        """Faucet VLAN VIP"""
        return '10.%u.0.254/%u' % (i + 1, self.NETPREFIX)

    @staticmethod
    def faucet_mac(i):
        """Faucet VLAN MAC"""
        return '00:00:00:00:00:%u%u' % (i + 1, i + 1)

    def host_ip_address(self, host_index, vlan_index):
        """Create a string of the host IP address"""
        if isinstance(vlan_index, tuple):
            vlan_index = vlan_index[0]
        return '10.%u.0.%u/%u' % (vlan_index + 1, host_index + 1,
                                  self.NETPREFIX)

    @staticmethod
    def vlan_name(i):
        """VLAN name"""
        return 'vlan-%i' % (i + 1)

    @staticmethod
    def vlan_vid(i):
        """VLAN VID value"""
        return (i + 1) * 100

    def host_ping(self, src_host, dst_ip):
        """Default method to ping from one host to an IP address"""
        self.one_ipv4_ping(src_host,
                           dst_ip,
                           require_host_learned=False,
                           retries=5,
                           timeout=1000)

    def set_host_ip(self, host, host_ip):
        """Default method for setting a hosts IP address"""
        host.setIP(str(host_ip.ip), prefixLen=self.NETPREFIX)

    def build_net(self,
                  n_dps=1,
                  n_vlans=1,
                  dp_links=None,
                  host_links=None,
                  host_vlans=None,
                  vlan_options=None,
                  dp_options=None,
                  host_options=None,
                  routers=None,
                  stack_roots=None,
                  include=None,
                  include_optional=None,
                  hw_dpid=None,
                  lacp=False):
        """
        Use the TopologyGenerator to generate the YAML configuration and create the network
        Args:
            n_dps: Number of DPs
            n_vlans: Number of VLANs
            dp_links (dict): dp index to dp index
            host_links (dict): host index to list of dp index
            host_vlans (dict): host index to vlan index
            vlan_options (dict): vlan_index to key, value dp options
            dp_options (dict): dp index to key, value dp options
            host_options (dict): Host index to host option key, values
            routers (dict): router index to list of vlan index
            stack_roots (dict): dp index to priority value (leave none for tagged links)
            include:
            include_optional:
            hw_dpid: DPID of hardware switch
            lacp: Use LACP trunk ports
        """
        if include is None:
            include = []
        if include_optional is None:
            include_optional = []
        self.NUM_DPS = n_dps
        self.dpids = [str(self.rand_dpid()) for _ in range(n_dps)]
        self.dpids[0] = self.dpid
        vlan_vids = {vlan: self.vlan_vid(vlan) for vlan in range(n_vlans)}
        self.topo = FaucetTopoGenerator(
            self.OVS_TYPE,
            self.ports_sock,
            self._test_name(),
            self.dpids,
            dp_links,
            host_links,
            host_vlans,
            vlan_vids,
            hw_dpid=self.hw_dpid,
            switch_map=self.switch_map,
            port_order=self.port_order,
        )
        self.port_maps = {
            dpid: self.create_port_map(dpid)
            for dpid in self.dpids
        }
        self.port_map = self.port_maps[self.dpid]
        self.CONFIG = self.get_config(dpids=self.dpids,
                                      hw_dpid=hw_dpid,
                                      hardware=self.hardware,
                                      ofchannel_log=self.debug_log_path,
                                      n_vlans=n_vlans,
                                      host_links=host_links,
                                      host_vlans=host_vlans,
                                      stack_roots=stack_roots,
                                      include=include,
                                      include_optional=include_optional,
                                      acls=self.acls(),
                                      acl_in_dp=self.acl_in_dp(),
                                      lacp=lacp,
                                      vlan_options=vlan_options,
                                      dp_options=dp_options,
                                      routers=routers,
                                      host_options=host_options)
        self.n_vlans = n_vlans
        self.dp_links = dp_links
        self.host_links = host_links
        self.host_vlans = host_vlans
        self.stack_roots = stack_roots
        self.routers = routers
        self.dp_options = dp_options
        self.host_options = host_options
        self.vlan_options = vlan_options

    def start_net(self):
        """
        Override start_net to create the faucet vips, the host information and set up the
            host routes for routed hosts
        """
        super(FaucetTopoTestBase, self).start_net()
        self.host_information = {}
        for host_id, host_name in self.topo.hosts_by_id.items():
            host_obj = self.net.get(host_name)
            vlan = self.host_vlans[host_id]
            ip_interface = ipaddress.ip_interface(
                self.host_ip_address(host_id, vlan))
            self.set_host_ip(host_obj, ip_interface)
            self.host_information[host_id] = {
                'host': host_obj,
                'ip': ip_interface,
                'vlan': vlan
            }

        self.faucet_vips = {}
        for vlan in range(self.n_vlans):
            self.faucet_vips[vlan] = ipaddress.ip_interface(
                self.faucet_vip(vlan))

        if self.routers:
            for src in self.host_information:
                src_host = self.host_information[src]['host']
                src_vlan = self.host_information[src]['vlan']
                src_ip = self.host_information[src]['ip']
                for dst in self.host_information:
                    if src != dst:
                        dst_host = self.host_information[dst]['host']
                        dst_vlan = self.host_information[dst]['vlan']
                        dst_ip = self.host_information[dst]['ip']
                        if src_vlan != dst_vlan and self.is_routed_vlans(
                                src_vlan, dst_vlan):
                            src_faucet_vip = self.faucet_vips[src_vlan]
                            dst_faucet_vip = self.faucet_vips[dst_vlan]
                            self.add_host_route(src_host, dst_ip,
                                                src_faucet_vip.ip)
                            self.add_host_route(dst_host, src_ip,
                                                dst_faucet_vip.ip)

    def get_config(self,
                   dpids=None,
                   hw_dpid=None,
                   hardware=None,
                   ofchannel_log=None,
                   n_vlans=1,
                   host_links=None,
                   host_vlans=None,
                   stack_roots=None,
                   include=None,
                   include_optional=None,
                   acls=None,
                   acl_in_dp=None,
                   lacp=False,
                   vlan_options=None,
                   dp_options=None,
                   routers=None,
                   host_options=None):
        """
        Args:
            dpids: List of DPIDs the dp indices in the configuration dictionaries refer to
            hw_dpid: DPID for connected hardware switch
            hardware:
            ofchannel_log: Debug log path
            n_vlans: Number of VLANs
            host_links (dict): host index to dp index
            host_vlans (dict): host index to vlan index
            stack_roots (dict): dp index to priority value (leave none for tagged links)
            include:
            include_optional:
            hw_dpid: DPID of hardware switch
            lacp: Use LACP trunk ports
            vlan_options (dict): vlan_index to key, value dp options
            dp_options (dict): dp index to key, value dp options
            routers (dict): router index to list of vlan index
            host_options (dict): Host index to host option key, values
        """
        if dpids is None:
            dpids = []
        if include is None:
            include = []
        if include_optional is None:
            include_optional = []
        if acls is None:
            acls = {}
        if acl_in_dp is None:
            acl_in_dp = {}

        def add_vlans(n_vlans, host_vlans, vlan_options):
            vlans_config = {}
            for vlan in range(n_vlans):
                n_tagged = 0
                n_untagged = 0
                for vlans in host_vlans.values():
                    if isinstance(vlans, int) and vlan == vlans:
                        n_untagged += 1
                    elif isinstance(vlans, tuple) and vlan in vlans:
                        n_tagged += 1
                vlans_config[self.vlan_name(vlan)] = {
                    'description':
                    '%s tagged, %s untagged' % (n_tagged, n_untagged),
                    'vid':
                    self.vlan_vid(vlan)
                }
            if vlan_options:
                for vlan, options in vlan_options.items():
                    for key, value in options.items():
                        vlans_config[self.vlan_name(vlan)][key] = value
            return vlans_config

        def add_routers(routers):
            router_config = {}
            for i, vlans in routers.items():
                router_config['router-%s' % i] = {
                    'vlans': [self.vlan_name(vlan) for vlan in vlans]
                }
            return router_config

        def add_acl_to_port(i, port, interfaces_config):
            if i in acl_in_dp and port in acl_in_dp[i]:
                interfaces_config[port]['acl_in'] = acl_in_dp[i][port]

        def add_dp(i, dpid, hw_dpid, ofchannel_log, group_table, n_vlans,
                   host_vlans, stack_roots, host_links, dpid_peer_links,
                   port_maps):
            dp_config = {
                'dp_id': int(dpid),
                'hardware': hardware if dpid == hw_dpid else 'Open vSwitch',
                'ofchannel_log':
                ofchannel_log + str(i) if ofchannel_log else None,
                'interfaces': {},
                'group_table': group_table,
            }

            if dp_options and i in dp_options:
                for key, value in dp_options[i].items():
                    dp_config[key] = value

            if stack_roots and i in stack_roots:
                dp_config['stack'] = {}
                dp_config['stack']['priority'] = stack_roots[i]

            interfaces_config = {}
            # Generate host links
            index = 1
            for host_id, links in host_links.items():
                if i in links:
                    n_links = links.count(i)
                    vlan = host_vlans[host_id]
                    if isinstance(vlan, int):
                        key = 'native_vlan'
                        value = self.vlan_name(vlan)
                    else:
                        key = 'tagged_vlans'
                        value = [self.vlan_name(vlan) for vlan in vlan]
                    for _ in range(n_links):
                        port = port_maps[dpid]['port_%d' % index]
                        interfaces_config[port] = {key: value}
                        if host_options and host_id in host_options:
                            for key, value in host_options[host_id].items():
                                interfaces_config[port][key] = value
                        index += 1
                        add_acl_to_port(i, port, interfaces_config)

            # Generate switch-switch links
            for link in dpid_peer_links:
                port, peer_dpid, peer_port = link.port, link.peer_dpid, link.peer_port
                interfaces_config[port] = {}
                if stack_roots:
                    interfaces_config[port].update({
                        'stack': {
                            'dp': self.dp_name(dpids.index(peer_dpid)),
                            'port': peer_port
                        }
                    })
                else:
                    tagged_vlans = [
                        self.vlan_name(vlan) for vlan in range(n_vlans)
                    ]
                    interfaces_config[port].update(
                        {'tagged_vlans': tagged_vlans})
                    if lacp:
                        interfaces_config[port].update({
                            'lacp': 1,
                            'lacp_active': True
                        })
                        dp_config['lacp_timeout'] = 10
                add_acl_to_port(i, port, interfaces_config)

            dp_config['interfaces'] = interfaces_config
            return dp_config

        config = {'version': 2}
        if include:
            config['include'] = list(include)
        if include_optional:
            config['include_optional'] = list(include_optional)
        config['acls'] = acls.copy()
        config['vlans'] = add_vlans(n_vlans, host_vlans, vlan_options)

        if routers:
            config['routers'] = add_routers(routers)

        dpid_names = {dpids[i]: self.dp_name(i) for i in range(len(dpids))}
        self.set_dpid_names(dpid_names)
        config['dps'] = {}
        for i, dpid in enumerate(dpids):
            config['dps'][self.dp_name(i)] = add_dp(
                i, dpid, hw_dpid, ofchannel_log, self.GROUP_TABLE, n_vlans,
                host_vlans, stack_roots, host_links,
                self.topo.dpid_peer_links(dpid), self.port_maps)

        return yaml.dump(config, default_flow_style=False)

    def verify_no_cable_errors(self):
        """Check that prometheus does not detect any stack cabling errors on all DPs"""
        i = 0
        for dpid in self.dpids:
            i += 1
            labels = {'dp_id': '0x%x' % int(dpid), 'dp_name': 'faucet-%u' % i}
            self.assertEqual(
                0,
                self.scrape_prometheus_var(var='stack_cabling_errors_total',
                                           labels=labels,
                                           default=None))
            self.assertGreater(
                self.scrape_prometheus_var(var='stack_probes_received_total',
                                           labels=labels), 0)

    def verify_stack_hosts(self, verify_bridge_local_rule=True, retries=3):
        """Verify hosts with stack LLDP messages"""
        lldp_cap_files = []
        for host in self.hosts_name_ordered():
            lldp_cap_file = os.path.join(self.tmpdir, '%s-lldp.cap' % host)
            lldp_cap_files.append(lldp_cap_file)
            host.cmd(
                timeout_cmd(
                    'tcpdump -U -n -c 1 -i %s -w %s ether proto 0x88CC and not ether src %s &'
                    % (host.defaultIntf(), host.MAC(), lldp_cap_file), 60))
        # should not flood LLDP from hosts
        self.verify_lldp_blocked(self.hosts_name_ordered())
        # hosts should see no LLDP probes
        self.verify_empty_caps(lldp_cap_files)
        if verify_bridge_local_rule:
            # Verify 802.1x flood block triggered.
            for dpid in self.dpids:
                self.wait_nonzero_packet_count_flow(
                    {'dl_dst': '01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0'},
                    dpid=dpid,
                    table_id=self._FLOOD_TABLE,
                    ofa_match=False)
        self.retry_net_ping(retries=retries)

    def stack_port_status(self, dpid, dp_name, port_no):
        """Return the status of a stack port from prometheus"""
        labels = self.port_labels(port_no)
        labels.update({'dp_id': '0x%x' % int(dpid), 'dp_name': dp_name})
        return self.scrape_prometheus_var('port_stack_state',
                                          labels=labels,
                                          default=None,
                                          dpid=dpid)

    def wait_for_stack_port_status(self,
                                   dpid,
                                   dp_name,
                                   port_no,
                                   status,
                                   timeout=25):
        """Wait until prometheus detects a stack port has a certain status"""
        labels = self.port_labels(port_no)
        labels.update({'dp_id': '0x%x' % int(dpid), 'dp_name': dp_name})
        if not self.wait_for_prometheus_var('port_stack_state',
                                            status,
                                            labels=labels,
                                            default=None,
                                            dpid=False,
                                            timeout=timeout):
            self.fail(
                'did not get expected dpid %x port %u port_stack_state %u' %
                (int(dpid), port_no, status))

    def one_stack_port_down(self, dpid, dp_name, port):
        """Set a stack port down and wait for prometheus to detect the change"""
        self.set_port_down(port, dpid, wait=False)
        self.wait_for_stack_port_status(dpid, dp_name, port, 4)

    def one_stack_port_up(self, dpid, dp_name, port):
        """Set a stack port up and wait for prometheus to detect the change"""
        self.set_port_up(port, dpid, wait=False)
        self.wait_for_stack_port_status(dpid, dp_name, port, 3)

    def verify_stack_up(self, prop=1.0, timeout=25):
        """Verify all stack ports are up"""
        for _ in range(timeout):
            links = 0
            links_up = 0
            for i, dpid in enumerate(self.dpids):
                dp_name = self.dp_name(i)
                for link in self.non_host_links(dpid):
                    status = self.stack_port_status(dpid, dp_name, link.port)
                    links += 1
                    if status == 3:  # up
                        links_up += 1
            prop_up = links_up / links
            if prop_up >= prop:
                return
            time.sleep(1)
        self.fail('not enough links up: %f / %f' % (links_up, links))

    def verify_one_stack_down(self, stack_offset_port, coldstart=False):
        """Test conditions when one stack port is down"""
        self.retry_net_ping()
        stack_port = self.non_host_links(self.dpid)[stack_offset_port].port
        remote_stack_port = self.non_host_links(
            self.dpid)[stack_offset_port].peer_port
        self.set_port_down(stack_port, wait=False)
        # self.dpids[1] is the intermediate switch.
        self.set_port_down(remote_stack_port, self.dpids[1], wait=False)
        # test case where one link is down when coldstarted.
        if coldstart:
            self.coldstart_conf()
        self.verify_stack_up(prop=0.75)
        self.verify_stack_hosts(verify_bridge_local_rule=False)
        # Broadcast works, and first switch doesn't see broadcast packet ins from stack.
        packet_in_before_broadcast = self.scrape_prometheus_var(
            'of_vlan_packet_ins')
        self.verify_broadcast()
        packet_in_after_broadcast = self.scrape_prometheus_var(
            'of_vlan_packet_ins')
        self.assertEqual(packet_in_before_broadcast, packet_in_after_broadcast)
        self.verify_no_cable_errors()

    def verify_no_arp_storm(self, ping_host, tcpdump_host):
        """Check that there is no excess ARP packets in the network"""
        num_arp_expected = self.topo.switch_to_switch_links * 2
        tcpdump_filter = 'arp and ether src %s' % ping_host.MAC()
        tcpdump_txt = self.tcpdump_helper(
            tcpdump_host,
            tcpdump_filter, [
                lambda: ping_host.cmd('arp -d %s' % tcpdump_host.IP()),
                lambda: ping_host.cmd('ping -c1 %s' % tcpdump_host.IP())
            ],
            packets=(num_arp_expected + 1))
        num_arp_received = len(
            re.findall(
                'who-has %s tell %s' % (tcpdump_host.IP(), ping_host.IP()),
                tcpdump_txt))
        self.assertTrue(num_arp_received)
        self.assertLessEqual(num_arp_received, num_arp_expected)

    def verify_stack_has_no_loop(self):
        """Ping between first and last hosts (by name) then verify there is no broadcast storm"""
        for ping_host, tcpdump_host in ((self.hosts_name_ordered()[0],
                                         self.hosts_name_ordered()[-1]),
                                        (self.hosts_name_ordered()[-1],
                                         self.hosts_name_ordered()[0])):
            self.verify_no_arp_storm(ping_host, tcpdump_host)

    def verify_all_stack_hosts(self):
        """Test conditions for stack hosts"""
        for _ in range(2):
            self.verify_stack_up()
            self.verify_no_cable_errors()
            self.verify_stack_hosts()
            self.verify_traveling_dhcp_mac()
            self.verify_unicast_not_looped()
            self.verify_no_bcast_to_self()
            self.verify_stack_has_no_loop()
            self.flap_all_switch_ports()

    def verify_tunnel_established(self,
                                  src_host,
                                  dst_host,
                                  other_host,
                                  packets=3):
        """Verify ICMP packets tunnelled from src to dst."""
        icmp_match = {'eth_type': IPV4_ETH, 'ip_proto': 1}
        self.wait_until_matching_flow(icmp_match,
                                      table_id=self._PORT_ACL_TABLE,
                                      ofa_match=False)
        tcpdump_text = self.tcpdump_helper(
            dst_host,
            'icmp[icmptype] == 8',
            [
                # need to set static ARP as only ICMP is tunnelled.
                lambda: src_host.cmd('arp -s %s %s' %
                                     (other_host.IP(), other_host.MAC())),
                lambda: src_host.cmd('ping -c%u -t1 %s' %
                                     (packets, other_host.IP()))
            ],
            packets=1,
            timeout=(packets + 1),
        )
        self.wait_nonzero_packet_count_flow(icmp_match,
                                            table_id=self._PORT_ACL_TABLE,
                                            ofa_match=False)
        self.assertTrue(
            re.search('%s: ICMP echo request' % other_host.IP(), tcpdump_text),
            'Tunnel was not established')

    def verify_one_broadcast(self, from_host, to_hosts):
        """Verify host connectivity via broadcast"""
        self.assertGreater(len(to_hosts), 1,
                           'Testing only one ext host is not useful')
        received_broadcasts = []
        for to_host in to_hosts:
            if self.verify_broadcast(hosts=(from_host, to_host),
                                     broadcast_expected=None):
                received_broadcasts.append(to_host)
        received_names = {host.name: host for host in received_broadcasts}
        self.assertEqual(
            len(received_broadcasts), 1,
            'Received not exactly one broadcast from %s: %s' %
            (from_host.name, received_names))

    def map_int_ext_hosts(self):
        """
        Obtains a list of the interal hosts, the external hosts and a dictionary
            of the internal and external hosts for each DP by DP name
        Returns int_hosts, ext_hosts, dp_hosts
        """
        int_hosts = []
        ext_hosts = []
        dp_hosts = {
            self.dp_name(dp_index): ([], [])
            for dp_index in range(self.NUM_DPS)
        }
        for host_id, options in self.host_options.items():
            host = self.host_information[host_id]['host']
            if options.get('loop_protect_external', False):
                ext_hosts.append(host)
                int_or_ext = 1
            else:
                int_hosts.append(host)
                int_or_ext = 0
            for link in self.host_links[host_id]:
                dp_hosts[self.dp_name(link)][int_or_ext].append(host)
        return set(int_hosts), set(ext_hosts), dp_hosts

    def verify_protected_connectivity(self):
        """
        Checks:
            - All internal hosts can reach other internal hosts
            - All internal hosts can reach exactly one external host
            - All external hosts cannot flood to each other
            - All external hosts can reach the internal hosts
        """
        self.verify_stack_up()
        int_hosts, ext_hosts, _ = self.map_int_ext_hosts()

        for int_host in int_hosts:
            # All internal hosts can reach other internal hosts.
            for other_int_host in int_hosts - {int_host}:
                self.verify_broadcast(hosts=(int_host, other_int_host),
                                      broadcast_expected=True)
                self.one_ipv4_ping(int_host, other_int_host.IP())

            # All internal hosts can reach exactly one external host.
            self.verify_one_broadcast(int_host, ext_hosts)

        for ext_host in ext_hosts:
            # All external hosts can reach internal hosts.
            for int_host in int_hosts:
                self.verify_broadcast(hosts=(ext_host, int_host),
                                      broadcast_expected=True)
                self.one_ipv4_ping(ext_host, int_host.IP())

            # All external hosts cannot flood to each other.
            for other_ext_host in ext_hosts - {ext_host}:
                self.verify_broadcast(hosts=(ext_host, other_ext_host),
                                      broadcast_expected=False)

    def set_externals_state(self, dp_name, externals_up):
        """Set the port up/down state of all external ports on a switch"""
        dp_conf = self._get_faucet_conf()['dps'][dp_name]
        for port_num, port_conf in dp_conf['interfaces'].items():
            if port_conf.get('loop_protect_external'):
                if externals_up:
                    self.set_port_up(port_num, dp_conf.get('dp_id'))
                else:
                    self.set_port_down(port_num, dp_conf.get('dp_id'))

    def validate_with_externals_down(self, dp_name):
        """Check situation when all externals on a given dp are down"""
        self.set_externals_state(dp_name, False)
        self.verify_protected_connectivity()
        self.set_externals_state(dp_name, True)

    def validate_with_externals_down_fails(self, dp_name):
        """Faucet code is not currently correct, so expect to fail."""
        # TODO: Fix faucet so the test inversion is no longer required.
        asserted = False
        try:
            self.validate_with_externals_down(dp_name)
        except AssertionError:
            asserted = True
        self.assertTrue(asserted, 'Did not fail as expected for %s' % dp_name)

    def verify_intervlan_routing(self):
        """Verify intervlan routing is possible"""
        for src in self.host_information:
            src_host = self.host_information[src]['host']
            src_vlan = self.host_information[src]['vlan']
            for dst in self.host_information:
                if dst > src:
                    dst_host = self.host_information[dst]['host']
                    dst_vlan = self.host_information[dst]['vlan']
                    dst_ip = self.host_information[dst]['ip']
                    if self.is_routed_vlans(src_vlan, dst_vlan):
                        src_faucet_vip = self.faucet_vips[src_vlan]
                        dst_faucet_vip = self.faucet_vips[dst_vlan]
                        self.host_ping(src_host, src_faucet_vip.ip)
                        self.host_ping(dst_host, dst_faucet_vip.ip)
                        self.host_ping(src_host, dst_ip.ip)
                        self.assertEqual(
                            self._ip_neigh(src_host,
                                           src_faucet_vip.ip, self.IPV),
                            self.faucet_mac(src_vlan))
                    elif src_vlan == dst_vlan:
                        self.host_ping(src_host, dst_ip.ip)

    def is_routed_vlans(self, vlan_a, vlan_b):
        """Return true if the two vlans share a router"""
        if self.routers:
            for vlans in self.routers.values():
                if (vlan_a in vlans and vlan_b in vlans):
                    return True
        return False

    def bcast_dst_blocked_helper(self, port, first_host, second_host,
                                 success_re, retries):
        """Helper for checking broadcast destination has been blocked"""
        tcpdump_filter = 'udp and ether src %s and ether dst %s' % (
            first_host.MAC(), "ff:ff:ff:ff:ff:ff")
        target_addr = str(self.faucet_vips[0].network.broadcast_address)
        for _ in range(retries):
            tcpdump_txt = self.tcpdump_helper(
                second_host,
                tcpdump_filter, [
                    partial(first_host.cmd,
                            ('date | socat - udp-datagram:%s:%d,broadcast' %
                             (target_addr, port)))
                ],
                packets=1)
            if re.search(success_re, tcpdump_txt):
                return True
            time.sleep(1)
        return False
예제 #17
0
 def build_net(self,
               n_dps=1,
               n_vlans=1,
               dp_links=None,
               host_links=None,
               host_vlans=None,
               vlan_options=None,
               dp_options=None,
               host_options=None,
               routers=None,
               stack_roots=None,
               include=None,
               include_optional=None,
               hw_dpid=None,
               lacp_trunk=False):
     """
     Use the TopologyGenerator to generate the YAML configuration and create the network
     Args:
         n_dps: Number of DPs
         n_vlans: Number of VLANs
         dp_links (dict): dp index to dp index
         host_links (dict): host index to list of dp index
         host_vlans (dict): host index to vlan index
         vlan_options (dict): vlan_index to key, value dp options
         dp_options (dict): dp index to key, value dp options
         host_options (dict): Host index to host option key, values
         routers (dict): router index to list of vlan index
         stack_roots (dict): dp index to priority value (leave none for tagged links)
         include:
         include_optional:
         hw_dpid: DPID of hardware switch
         lacp_trunk: Use LACP trunk ports
     """
     if include is None:
         include = []
     if include_optional is None:
         include_optional = []
     self.NUM_DPS = n_dps
     self.dpids = [str(self.rand_dpid()) for _ in range(n_dps)]
     self.dpids[0] = self.dpid
     vlan_vids = {vlan: self.vlan_vid(vlan) for vlan in range(n_vlans)}
     self.topo = FaucetTopoGenerator(self.OVS_TYPE,
                                     self.ports_sock,
                                     self._test_name(),
                                     self.dpids,
                                     dp_links,
                                     host_links,
                                     host_vlans,
                                     vlan_vids,
                                     hw_dpid=self.hw_dpid,
                                     switch_map=self.switch_map,
                                     port_order=self.port_order,
                                     start_port=self.start_port)
     self.port_maps = {
         dpid: self.create_port_map(dpid)
         for dpid in self.dpids
     }
     self.port_map = self.port_maps[self.dpid]
     self.CONFIG = self.get_config(dpids=self.dpids,
                                   hw_dpid=hw_dpid,
                                   hardware=self.hardware,
                                   ofchannel_log=self.debug_log_path,
                                   n_vlans=n_vlans,
                                   host_links=host_links,
                                   host_vlans=host_vlans,
                                   stack_roots=stack_roots,
                                   include=include,
                                   include_optional=include_optional,
                                   acls=self.acls(),
                                   acl_in_dp=self.acl_in_dp(),
                                   lacp_trunk=lacp_trunk,
                                   vlan_options=vlan_options,
                                   dp_options=dp_options,
                                   routers=routers,
                                   host_options=host_options)
     self.n_vlans = n_vlans
     self.dp_links = dp_links
     self.host_links = host_links
     self.host_vlans = host_vlans
     self.stack_roots = stack_roots
     self.routers = routers
     self.dp_options = dp_options
     self.host_options = host_options
     self.vlan_options = vlan_options
예제 #18
0
class FaucetTopoTestBase(FaucetTestBase):
    """
    Extension to the base test for the integration test suite to help set up arbitrary topologies
    This is supposed to be called with clib.mininet_topo_generator to take networkx graph
        generators and produce a set of switch-switch links and host-switch links
    """

    NETPREFIX = 24
    IPV = 4
    GROUP_TABLE = False

    NUM_DPS = 2
    NUM_HOSTS = 4
    LINKS_PER_HOST = 1

    LACP_TIMEOUT = 10

    dpids = None
    port_maps = None
    n_vlans = 0
    dp_links = None
    host_links = None
    host_vlans = None
    stack_roots = None
    routers = None
    dp_options = None
    host_options = None
    vlan_options = None
    host_information = None
    faucet_vips = None

    def non_host_links(self, dpid):
        """Return dpid peer links from topo"""
        return self.topo.dpid_peer_links(dpid)

    @staticmethod
    def get_config_header(_config_global, _debug_log, _dpid, _hardware):
        """Don't generate standard config file header."""
        return ''

    @staticmethod
    def acls():
        """Dictionary of ACLs"""
        return {}

    @staticmethod
    def acl_in_dp():
        """Dictionary of DP & port map to ACL"""
        return {}

    @staticmethod
    def dp_name(i):
        """DP config name"""
        return 'faucet-%u' % (i + 1)

    def faucet_vip(self, i):
        """Faucet VLAN VIP"""
        return '10.%u.0.254/%u' % (i + 1, self.NETPREFIX)

    @staticmethod
    def faucet_mac(i):
        """Faucet VLAN MAC"""
        return '00:00:00:00:00:%u%u' % (i + 1, i + 1)

    def host_ip_address(self, host_index, vlan_index):
        """Create a string of the host IP address"""
        if isinstance(vlan_index, tuple):
            vlan_index = vlan_index[0]
        return '10.%u.0.%u/%u' % (vlan_index + 1, host_index + 1,
                                  self.NETPREFIX)

    @staticmethod
    def vlan_name(i):
        """VLAN name"""
        return 'vlan-%i' % (i + 1)

    @staticmethod
    def vlan_vid(i):
        """VLAN VID value"""
        return (i + 1) * 100

    def host_ping(self, src_host, dst_ip, intf=None):
        """Default method to ping from one host to an IP address"""
        self.one_ipv4_ping(src_host,
                           dst_ip,
                           require_host_learned=False,
                           retries=5,
                           timeout=1000,
                           intf=intf)

    def set_host_ip(self, host, host_ip):
        """Default method for setting a hosts IP address"""
        host.setIP(str(host_ip.ip), prefixLen=self.NETPREFIX)

    def build_net(self,
                  n_dps=1,
                  n_vlans=1,
                  dp_links=None,
                  host_links=None,
                  host_vlans=None,
                  vlan_options=None,
                  dp_options=None,
                  host_options=None,
                  routers=None,
                  stack_roots=None,
                  include=None,
                  include_optional=None,
                  hw_dpid=None,
                  lacp_trunk=False):
        """
        Use the TopologyGenerator to generate the YAML configuration and create the network
        Args:
            n_dps: Number of DPs
            n_vlans: Number of VLANs
            dp_links (dict): dp index to dp index
            host_links (dict): host index to list of dp index
            host_vlans (dict): host index to vlan index
            vlan_options (dict): vlan_index to key, value dp options
            dp_options (dict): dp index to key, value dp options
            host_options (dict): Host index to host option key, values
            routers (dict): router index to list of vlan index
            stack_roots (dict): dp index to priority value (leave none for tagged links)
            include:
            include_optional:
            hw_dpid: DPID of hardware switch
            lacp_trunk: Use LACP trunk ports
        """
        if include is None:
            include = []
        if include_optional is None:
            include_optional = []
        self.NUM_DPS = n_dps
        self.dpids = [str(self.rand_dpid()) for _ in range(n_dps)]
        self.dpids[0] = self.dpid
        vlan_vids = {vlan: self.vlan_vid(vlan) for vlan in range(n_vlans)}
        self.topo = FaucetTopoGenerator(self.OVS_TYPE,
                                        self.ports_sock,
                                        self._test_name(),
                                        self.dpids,
                                        dp_links,
                                        host_links,
                                        host_vlans,
                                        vlan_vids,
                                        hw_dpid=self.hw_dpid,
                                        switch_map=self.switch_map,
                                        port_order=self.port_order,
                                        start_port=self.start_port)
        self.port_maps = {
            dpid: self.create_port_map(dpid)
            for dpid in self.dpids
        }
        self.port_map = self.port_maps[self.dpid]
        self.CONFIG = self.get_config(dpids=self.dpids,
                                      hw_dpid=hw_dpid,
                                      hardware=self.hardware,
                                      ofchannel_log=self.debug_log_path,
                                      n_vlans=n_vlans,
                                      host_links=host_links,
                                      host_vlans=host_vlans,
                                      stack_roots=stack_roots,
                                      include=include,
                                      include_optional=include_optional,
                                      acls=self.acls(),
                                      acl_in_dp=self.acl_in_dp(),
                                      lacp_trunk=lacp_trunk,
                                      vlan_options=vlan_options,
                                      dp_options=dp_options,
                                      routers=routers,
                                      host_options=host_options)
        self.n_vlans = n_vlans
        self.dp_links = dp_links
        self.host_links = host_links
        self.host_vlans = host_vlans
        self.stack_roots = stack_roots
        self.routers = routers
        self.dp_options = dp_options
        self.host_options = host_options
        self.vlan_options = vlan_options

    def start_net(self):
        """
        Override start_net to create the faucet vips, the host information and set up the
            host routes for routed hosts
        """
        super(FaucetTopoTestBase, self).start_net()
        # Create a dictionary of host information that might be used in a test later on.
        # This makes it easier to retrieve certain information and consolidates it into one
        #   location.
        self.host_information = {}
        for host_id, host_name in self.topo.hosts_by_id.items():
            host_obj = self.net.get(host_name)
            vlan = self.host_vlans[host_id]
            ip_interface = ipaddress.ip_interface(
                self.host_ip_address(host_id, vlan))
            self.set_host_ip(host_obj, ip_interface)
            self.host_information[host_id] = {
                'host': host_obj,
                'ip': ip_interface,
                'mac': host_obj.MAC(),
                'vlan': vlan,
                'bond': None,
                'ports': {}
            }
        # Add information of hosts chosen dpid, port map values
        # TODO: This redoes logic from get_config()
        for i, dpid in enumerate(self.dpids):
            index = 1
            for host_id, links in self.host_links.items():
                if i in links:
                    n_links = links.count(i)
                    for _ in range(n_links):
                        port = self.port_maps[dpid]['port_%d' % index]
                        self.host_information[host_id]['ports'].setdefault(
                            dpid, [])
                        self.host_information[host_id]['ports'][dpid].append(
                            port)
                        index += 1
        # Store faucet vip interfaces
        self.faucet_vips = {}
        for vlan in range(self.n_vlans):
            self.faucet_vips[vlan] = ipaddress.ip_interface(
                self.faucet_vip(vlan))
        # Setup the linux bonds for LACP connected hosts
        self.setup_lacp_bonds()
        # Add host routes to hosts for inter vlan routing
        self.setup_intervlan_host_routes()

    def setup_lacp_bonds(self):
        """Search through host options for lacp hosts and configure accordingly"""
        if not self.host_options:
            return
        bond_index = 1
        for host_id, options in self.host_options.items():
            if 'lacp' in options:
                host = self.host_information[host_id]['host']
                # LACP must be configured with host ports down
                for dpid, ports in self.host_information[host_id][
                        'ports'].items():
                    for port in ports:
                        self.set_port_down(port, dpid)
                orig_ip = host.IP()
                lacp_switches = [
                    self.net.switches[i] for i in self.host_links[host_id]
                ]
                bond_members = [
                    pair[0].name for switch in lacp_switches
                    for pair in host.connectionsTo(switch)
                ]
                bond_name = 'bond%u' % (bond_index)
                self.host_information[host_id]['bond'] = bond_name
                for bond_member in bond_members:
                    # Deconfigure bond members
                    self.quiet_commands(
                        host, ('ip link set %s down' % bond_member,
                               'ip address flush dev %s' % bond_member))
                # Configure bond interface
                self.quiet_commands(
                    host, (('ip link add %s address 0e:00:00:00:00:99 '
                            'type bond mode 802.3ad lacp_rate fast miimon 100 '
                            'xmit_hash_policy layer2+3') %
                           (bond_name), 'ip add add %s/%s dev %s' %
                           (orig_ip, self.NETPREFIX, bond_name),
                           'ip link set %s up' % bond_name))
                # Add bond members
                for bond_member in bond_members:
                    self.quiet_commands(host, ('ip link set dev %s master %s' %
                                               (bond_member, bond_name), ))
                bond_index += 1
                # Return the ports to UP
                for dpid, ports in self.host_information[host_id][
                        'ports'].items():
                    for port in ports:
                        self.set_port_up(port, dpid)

    def setup_intervlan_host_routes(self):
        """Configure host routes between hosts that belong on routed VLANs"""
        if self.routers:
            for src in self.host_information:
                src_host = self.host_information[src]['host']
                src_vlan = self.host_information[src]['vlan']
                src_ip = self.host_information[src]['ip']
                for dst in self.host_information:
                    if src != dst:
                        dst_host = self.host_information[dst]['host']
                        dst_vlan = self.host_information[dst]['vlan']
                        dst_ip = self.host_information[dst]['ip']
                        if src_vlan != dst_vlan and self.is_routed_vlans(
                                src_vlan, dst_vlan):
                            src_faucet_vip = self.faucet_vips[src_vlan]
                            dst_faucet_vip = self.faucet_vips[dst_vlan]
                            self.add_host_route(src_host, dst_ip,
                                                src_faucet_vip.ip)
                            self.add_host_route(dst_host, src_ip,
                                                dst_faucet_vip.ip)

    def get_config(self,
                   dpids=None,
                   hw_dpid=None,
                   hardware=None,
                   ofchannel_log=None,
                   n_vlans=1,
                   host_links=None,
                   host_vlans=None,
                   stack_roots=None,
                   include=None,
                   include_optional=None,
                   acls=None,
                   acl_in_dp=None,
                   lacp_trunk=False,
                   vlan_options=None,
                   dp_options=None,
                   routers=None,
                   host_options=None):
        """
        Args:
            dpids: List of DPIDs the dp indices in the configuration dictionaries refer to
            hw_dpid: DPID for connected hardware switch
            hardware:
            ofchannel_log: Debug log path
            n_vlans: Number of VLANs
            host_links (dict): host index to dp index
            host_vlans (dict): host index to vlan index
            stack_roots (dict): dp index to priority value (leave none for tagged links)
            include:
            include_optional:
            hw_dpid: DPID of hardware switch
            lacp_trunk: Use LACP trunk ports
            vlan_options (dict): vlan_index to key, value dp options
            dp_options (dict): dp index to key, value dp options
            routers (dict): router index to list of vlan index
            host_options (dict): Host index to host option key, values
        """
        if dpids is None:
            dpids = []
        if include is None:
            include = []
        if include_optional is None:
            include_optional = []
        if acls is None:
            acls = {}
        if acl_in_dp is None:
            acl_in_dp = {}

        def add_vlans(n_vlans, host_vlans, vlan_options):
            vlans_config = {}
            for vlan in range(n_vlans):
                n_tagged = 0
                n_untagged = 0
                for vlans in host_vlans.values():
                    if isinstance(vlans, int) and vlan == vlans:
                        n_untagged += 1
                    elif isinstance(vlans, tuple) and vlan in vlans:
                        n_tagged += 1
                vlans_config[self.vlan_name(vlan)] = {
                    'description':
                    '%s tagged, %s untagged' % (n_tagged, n_untagged),
                    'vid':
                    self.vlan_vid(vlan)
                }
            if vlan_options:
                for vlan, options in vlan_options.items():
                    for key, value in options.items():
                        vlans_config[self.vlan_name(vlan)][key] = value
            return vlans_config

        def add_routers(routers):
            router_config = {}
            for i, vlans in routers.items():
                router_config['router-%s' % i] = {
                    'vlans': [self.vlan_name(vlan) for vlan in vlans]
                }
            return router_config

        def add_acl_to_port(i, port, interfaces_config):
            if i in acl_in_dp and port in acl_in_dp[i]:
                interfaces_config[port]['acl_in'] = acl_in_dp[i][port]

        def add_dp(i, dpid, hw_dpid, ofchannel_log, group_table, n_vlans,
                   host_vlans, stack_roots, host_links, dpid_peer_links,
                   port_maps):
            dp_config = {
                'dp_id': int(dpid),
                'hardware': hardware if dpid == hw_dpid else 'Open vSwitch',
                'ofchannel_log':
                ofchannel_log + str(i) if ofchannel_log else None,
                'interfaces': {},
                'group_table': group_table,
            }

            if dp_options and i in dp_options:
                for key, value in dp_options[i].items():
                    dp_config[key] = value

            if stack_roots and i in stack_roots:
                dp_config['stack'] = {}
                dp_config['stack']['priority'] = stack_roots[i]  # pytype: disable=unsupported-operands

            interfaces_config = {}
            # Generate host links
            index = 1
            for host_id, links in host_links.items():
                if i in links:
                    n_links = links.count(i)
                    vlan = host_vlans[host_id]
                    if isinstance(vlan, int):
                        key = 'native_vlan'
                        value = self.vlan_name(vlan)
                    else:
                        key = 'tagged_vlans'
                        value = [self.vlan_name(vlan) for vlan in vlan]
                    for _ in range(n_links):
                        port = port_maps[dpid]['port_%d' % index]
                        interfaces_config[port] = {key: value}
                        if host_options and host_id in host_options:
                            for option_key, option_value in host_options[
                                    host_id].items():
                                interfaces_config[port][
                                    option_key] = option_value
                        index += 1
                        add_acl_to_port(i, port, interfaces_config)

            # Generate switch-switch links
            for link in dpid_peer_links:
                port, peer_dpid, peer_port = link.port, link.peer_dpid, link.peer_port
                interfaces_config[port] = {}
                if stack_roots:
                    interfaces_config[port].update({
                        'stack': {
                            'dp': self.dp_name(dpids.index(peer_dpid)),
                            'port': peer_port
                        }
                    })
                else:
                    tagged_vlans = [
                        self.vlan_name(vlan) for vlan in range(n_vlans)
                    ]
                    interfaces_config[port].update(
                        {'tagged_vlans': tagged_vlans})
                    if lacp_trunk:
                        interfaces_config[port].update({
                            'lacp': 1,
                            'lacp_active': True
                        })
                        dp_config['lacp_timeout'] = 10
                add_acl_to_port(i, port, interfaces_config)

            dp_config['interfaces'] = interfaces_config
            return dp_config

        config = {'version': 2}
        if include:
            config['include'] = list(include)
        if include_optional:
            config['include_optional'] = list(include_optional)
        config['acls'] = acls.copy()
        config['vlans'] = add_vlans(n_vlans, host_vlans, vlan_options)

        if routers:
            config['routers'] = add_routers(routers)

        dpid_names = {dpids[i]: self.dp_name(i) for i in range(len(dpids))}
        self.set_dpid_names(dpid_names)
        config['dps'] = {}
        for i, dpid in enumerate(dpids):
            config['dps'][self.dp_name(i)] = add_dp(
                i, dpid, hw_dpid, ofchannel_log, self.GROUP_TABLE, n_vlans,
                host_vlans, stack_roots, host_links,
                self.topo.dpid_peer_links(dpid), self.port_maps)

        return yaml.dump(config, default_flow_style=False)

    def debug(self):
        """Print host information when debugging"""
        try:
            super(FaucetTopoTestBase, self).debug()
        except Exception:
            pprint.pprint(self.host_information)
            raise

    def verify_no_cable_errors(self):
        """Check that prometheus does not detect any stack cabling errors on all DPs"""
        i = 0
        for dpid in self.dpids:
            i += 1
            labels = {'dp_id': '0x%x' % int(dpid), 'dp_name': 'faucet-%u' % i}
            self.assertEqual(
                0,
                self.scrape_prometheus_var(var='stack_cabling_errors_total',
                                           labels=labels,
                                           default=None))
            self.assertGreater(
                self.scrape_prometheus_var(var='stack_probes_received_total',
                                           labels=labels), 0)

    def verify_stack_hosts(self, verify_bridge_local_rule=True, retries=3):
        """Verify hosts with stack LLDP messages"""
        lldp_cap_files = []
        for host in self.hosts_name_ordered():
            lldp_cap_file = os.path.join(self.tmpdir, '%s-lldp.cap' % host)
            lldp_cap_files.append(lldp_cap_file)
            host.cmd(
                timeout_cmd(
                    'tcpdump -U -n -c 1 -i %s -w %s ether proto 0x88CC and not ether src %s &'
                    % (host.defaultIntf(), host.MAC(), lldp_cap_file), 60))
        # should not flood LLDP from hosts
        self.verify_lldp_blocked(self.hosts_name_ordered())
        # hosts should see no LLDP probes
        self.verify_empty_caps(lldp_cap_files)
        if verify_bridge_local_rule:
            # Verify 802.1x flood block triggered.
            for dpid in self.dpids:
                self.wait_nonzero_packet_count_flow(
                    {'dl_dst': '01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0'},
                    dpid=dpid,
                    table_id=self._FLOOD_TABLE,
                    ofa_match=False)
        self.retry_net_ping(retries=retries)

    def stack_port_status(self, dpid, dp_name, port_no):
        """Return the status of a stack port from prometheus"""
        labels = self.port_labels(port_no)
        labels.update({'dp_id': '0x%x' % int(dpid), 'dp_name': dp_name})
        return self.scrape_prometheus_var('port_stack_state',
                                          labels=labels,
                                          default=None,
                                          dpid=dpid)

    def wait_for_stack_port_status(self,
                                   dpid,
                                   dp_name,
                                   port_no,
                                   status,
                                   timeout=25):
        """Wait until prometheus detects a stack port has a certain status"""
        labels = self.port_labels(port_no)
        labels.update({'dp_id': '0x%x' % int(dpid), 'dp_name': dp_name})
        if not self.wait_for_prometheus_var('port_stack_state',
                                            status,
                                            labels=labels,
                                            default=None,
                                            dpid=False,
                                            timeout=timeout):
            self.fail(
                'did not get expected dpid %x port %u port_stack_state %u' %
                (int(dpid), port_no, status))

    def one_stack_port_down(self, dpid, dp_name, port):
        """Set a stack port down and wait for prometheus to detect the change"""
        self.set_port_down(port, dpid, wait=False)
        self.wait_for_stack_port_status(dpid, dp_name, port, 4)

    def one_stack_port_up(self, dpid, dp_name, port):
        """Set a stack port up and wait for prometheus to detect the change"""
        self.set_port_up(port, dpid, wait=False)
        self.wait_for_stack_port_status(dpid, dp_name, port, 3)

    def verify_stack_up(self, prop=1.0, timeout=25):
        """Verify all stack ports are up"""
        for _ in range(timeout):
            links = 0
            links_up = 0
            for i, dpid in enumerate(self.dpids):
                dp_name = self.dp_name(i)
                for link in self.non_host_links(dpid):
                    status = self.stack_port_status(dpid, dp_name, link.port)
                    links += 1
                    if status == 3:  # up
                        links_up += 1
            prop_up = links_up / links
            if prop_up >= prop:
                return
            time.sleep(1)
        self.fail('not enough links up: %f / %f' % (links_up, links))

    def verify_one_stack_down(self, stack_offset_port, coldstart=False):
        """Test conditions when one stack port is down"""
        self.retry_net_ping()
        stack_port = self.non_host_links(self.dpid)[stack_offset_port].port
        remote_stack_port = self.non_host_links(
            self.dpid)[stack_offset_port].peer_port
        self.set_port_down(stack_port, wait=False)
        # self.dpids[1] is the intermediate switch.
        self.set_port_down(remote_stack_port, self.dpids[1], wait=False)
        # test case where one link is down when coldstarted.
        if coldstart:
            self.coldstart_conf()
        self.verify_stack_up(prop=0.75)
        self.verify_stack_hosts(verify_bridge_local_rule=False)
        # Broadcast works, and first switch doesn't see broadcast packet ins from stack.
        packet_in_before_broadcast = self.scrape_prometheus_var(
            'of_vlan_packet_ins')
        self.verify_broadcast()
        packet_in_after_broadcast = self.scrape_prometheus_var(
            'of_vlan_packet_ins')
        self.assertEqual(packet_in_before_broadcast, packet_in_after_broadcast)
        self.verify_no_cable_errors()

    def verify_no_arp_storm(self, ping_host, tcpdump_host):
        """Check that there is no excess ARP packets in the network"""
        num_arp_expected = self.topo.switch_to_switch_links * 2
        tcpdump_filter = 'arp and ether src %s' % ping_host.MAC()
        tcpdump_txt = self.tcpdump_helper(
            tcpdump_host,
            tcpdump_filter, [
                lambda: ping_host.cmd('arp -d %s' % tcpdump_host.IP()),
                lambda: ping_host.cmd('ping -c1 %s' % tcpdump_host.IP())
            ],
            packets=(num_arp_expected + 1))
        num_arp_received = len(
            re.findall(
                'who-has %s tell %s' % (tcpdump_host.IP(), ping_host.IP()),
                tcpdump_txt))
        self.assertTrue(num_arp_received)
        self.assertLessEqual(num_arp_received, num_arp_expected)

    def verify_stack_has_no_loop(self):
        """Ping between first and last hosts (by name) then verify there is no broadcast storm"""
        for ping_host, tcpdump_host in ((self.hosts_name_ordered()[0],
                                         self.hosts_name_ordered()[-1]),
                                        (self.hosts_name_ordered()[-1],
                                         self.hosts_name_ordered()[0])):
            self.verify_no_arp_storm(ping_host, tcpdump_host)

    def verify_all_stack_hosts(self):
        """Test conditions for stack hosts"""
        for _ in range(2):
            self.verify_stack_up()
            self.verify_no_cable_errors()
            self.verify_stack_hosts()
            self.verify_traveling_dhcp_mac()
            self.verify_unicast_not_looped()
            self.verify_no_bcast_to_self()
            self.verify_stack_has_no_loop()
            self.flap_all_switch_ports()

    def verify_tunnel_established(self,
                                  src_host,
                                  dst_host,
                                  other_host,
                                  packets=3):
        """Verify ICMP packets tunnelled from src to dst."""
        icmp_match = {'eth_type': IPV4_ETH, 'ip_proto': 1}
        self.wait_until_matching_flow(icmp_match,
                                      table_id=self._PORT_ACL_TABLE,
                                      ofa_match=False)
        tcpdump_text = self.tcpdump_helper(
            dst_host,
            'icmp[icmptype] == 8',
            [
                # need to set static ARP as only ICMP is tunnelled.
                lambda: src_host.cmd('arp -s %s %s' %
                                     (other_host.IP(), other_host.MAC())),
                lambda: src_host.cmd('ping -c%u -t1 %s' %
                                     (packets, other_host.IP()))
            ],
            packets=1,
            timeout=(packets + 1),
        )
        self.wait_nonzero_packet_count_flow(icmp_match,
                                            table_id=self._PORT_ACL_TABLE,
                                            ofa_match=False)
        self.assertTrue(
            re.search('%s: ICMP echo request' % other_host.IP(), tcpdump_text),
            'Tunnel was not established')

    def verify_one_broadcast(self, from_host, to_hosts):
        """Verify host connectivity via broadcast"""
        self.assertGreater(len(to_hosts), 1,
                           'Testing only one ext host is not useful')
        received_broadcasts = []
        for to_host in to_hosts:
            if self.verify_broadcast(hosts=(from_host, to_host),
                                     broadcast_expected=None):
                received_broadcasts.append(to_host)
        received_names = {host.name: host for host in received_broadcasts}
        self.assertEqual(
            len(received_broadcasts), 1,
            'Received not exactly one broadcast from %s: %s' %
            (from_host.name, received_names))

    def map_int_ext_hosts(self):
        """
        Obtains a list of the interal hosts, the external hosts and a dictionary
            of the internal and external hosts for each DP by DP name
        Returns int_hosts, ext_hosts, dp_hosts
        """
        int_hosts = []
        ext_hosts = []
        dp_hosts = {
            self.dp_name(dp_index): ([], [])
            for dp_index in range(self.NUM_DPS)
        }
        for host_id, options in self.host_options.items():
            host = self.host_information[host_id]['host']
            if options.get('loop_protect_external', False):
                ext_hosts.append(host)
                int_or_ext = 1
            else:
                int_hosts.append(host)
                int_or_ext = 0
            for link in self.host_links[host_id]:
                dp_hosts[self.dp_name(link)][int_or_ext].append(host)
        return set(int_hosts), set(ext_hosts), dp_hosts

    def verify_protected_connectivity(self):
        """
        Checks:
            - All internal hosts can reach other internal hosts
            - All internal hosts can reach exactly one external host
            - All external hosts cannot flood to each other
            - All external hosts can reach the internal hosts
        """
        self.verify_stack_up()
        int_hosts, ext_hosts, _ = self.map_int_ext_hosts()

        for int_host in int_hosts:
            # All internal hosts can reach other internal hosts.
            for other_int_host in int_hosts - {int_host}:
                self.verify_broadcast(hosts=(int_host, other_int_host),
                                      broadcast_expected=True)
                self.one_ipv4_ping(int_host, other_int_host.IP())

            # All internal hosts can reach exactly one external host.
            self.verify_one_broadcast(int_host, ext_hosts)

        for ext_host in ext_hosts:
            # All external hosts can reach internal hosts.
            for int_host in int_hosts:
                self.verify_broadcast(hosts=(ext_host, int_host),
                                      broadcast_expected=True)
                self.one_ipv4_ping(ext_host, int_host.IP())

            # All external hosts cannot flood to each other.
            for other_ext_host in ext_hosts - {ext_host}:
                self.verify_broadcast(hosts=(ext_host, other_ext_host),
                                      broadcast_expected=False)

    def set_externals_state(self, dp_name, externals_up):
        """Set the port up/down state of all external ports on a switch"""
        dp_conf = self._get_faucet_conf()['dps'][dp_name]
        for port_num, port_conf in dp_conf['interfaces'].items():
            if port_conf.get('loop_protect_external'):
                if externals_up:
                    self.set_port_up(port_num, dp_conf.get('dp_id'))
                else:
                    self.set_port_down(port_num, dp_conf.get('dp_id'))

    def validate_with_externals_down(self, dp_name):
        """Check situation when all externals on a given dp are down"""
        self.set_externals_state(dp_name, False)
        self.verify_protected_connectivity()
        self.set_externals_state(dp_name, True)

    def validate_with_externals_down_fails(self, dp_name):
        """Faucet code is not currently correct, so expect to fail."""
        # TODO: Fix faucet so the test inversion is no longer required.
        asserted = False
        try:
            self.validate_with_externals_down(dp_name)
        except AssertionError:
            asserted = True
        self.assertTrue(asserted, 'Did not fail as expected for %s' % dp_name)

    def verify_intervlan_routing(self):
        """Verify intervlan routing but for LAG host use bond interface"""
        for src in self.host_information:
            for dst in self.host_information:
                if dst > src:
                    self.check_host_connectivity_by_id(src, dst)

    def check_host_connectivity_by_id(self, src_id, dst_id):
        """Ping from src to dst with host_id parameters if they should be able to"""
        src_host, src_ip, _, src_vlan, src_bond, _ = self.host_information[
            src_id].values()
        dst_host, dst_ip, _, dst_vlan, dst_bond, _ = self.host_information[
            dst_id].values()
        connectivity = src_vlan == dst_vlan or self.is_routed_vlans(
            src_vlan, dst_vlan)
        if self.is_routed_vlans(src_vlan, dst_vlan):
            src_vip = self.faucet_vips[src_vlan]
            dst_vip = self.faucet_vips[dst_vlan]
            self.host_ping(src_host, src_vip.ip, src_bond)  # pytype: disable=attribute-error
            self.host_ping(dst_host, dst_vip.ip, dst_bond)  # pytype: disable=attribute-error
        if connectivity:
            self.host_ping(src_host, dst_ip.ip, src_bond)  # pytype: disable=attribute-error
            self.host_ping(dst_host, src_ip.ip, dst_bond)  # pytype: disable=attribute-error

    def is_routed_vlans(self, vlan_a, vlan_b):
        """Return true if the two vlans share a router"""
        if self.routers:
            for vlans in self.routers.values():
                if (vlan_a in vlans and vlan_b in vlans):
                    return True
        return False

    def bcast_dst_blocked_helper(self, port, first_host, second_host,
                                 success_re, retries):
        """Helper for checking broadcast destination has been blocked"""
        tcpdump_filter = 'udp and ether src %s and ether dst %s' % (
            first_host.MAC(), "ff:ff:ff:ff:ff:ff")
        target_addr = str(self.faucet_vips[0].network.broadcast_address)
        for _ in range(retries):
            tcpdump_txt = self.tcpdump_helper(
                second_host,
                tcpdump_filter, [
                    partial(first_host.cmd,
                            ('date | socat - udp-datagram:%s:%d,broadcast' %
                             (target_addr, port)))
                ],
                packets=1)
            if re.search(success_re, tcpdump_txt):
                return True
            time.sleep(1)
        return False

    def get_expected_synced_states(self, host_id):
        """Return the list of regex string for the expected sync state of a LACP LAG connection"""
        synced_state_list = []
        oper_key = self.host_options[host_id]['lacp']
        lacp_ports = [
            port for ports in self.host_information[host_id]['ports'].values()
            for port in ports
        ]
        for port in lacp_ports:
            synced_state_txt = r"""
Slave Interface: \S+
MII Status: up
Speed: \d+ Mbps
Duplex: full
Link Failure Count: \d+
Permanent HW addr: \S+
Slave queue ID: 0
Aggregator ID: \d+
Actor Churn State: monitoring
Partner Churn State: monitoring
Actor Churned Count: 0
Partner Churned Count: 0
details actor lacp pdu:
    system priority: 65535
    system mac address: 0e:00:00:00:00:99
    port key: \d+
    port priority: 255
    port number: \d+
    port state: 63
details partner lacp pdu:
    system priority: 65535
    system mac address: 0e:00:00:00:00:01
    oper key: %d
    port priority: 255
    port number: %d
    port state: 62
""".strip() % (oper_key, port)
            synced_state_list.append(synced_state_txt)
        return synced_state_list

    def prom_lacp_up_ports(self, dpid):
        """Get the number of up LAG ports according to Prometheus for a dpid"""
        lacp_up_ports = 0
        for host_id, options in self.host_options.items():
            # Find LACP hosts
            for key in options.keys():
                if key == 'lacp':
                    # Is LACP host
                    host_information = self.host_information[host_id]
                    if dpid in host_information['ports']:
                        # LACP host has links to dpid
                        lacp_ports = host_information['ports'][dpid]
                        for port in lacp_ports:
                            # Obtain up LACP ports for that dpid
                            port_labels = self.port_labels(port)
                            lacp_state = self.scrape_prometheus_var(
                                'port_lacp_state',
                                port_labels,
                                default=0,
                                dpid=dpid)
                            lacp_up_ports += 1 if lacp_state == 3 else 0
        return lacp_up_ports

    def verify_num_lag_up_ports(self, expected_up_ports, dpid):
        """Checks to see if Prometheus has the expected number of up LAG ports on the specified DP"""
        for _ in range(self.LACP_TIMEOUT * 10):
            if self.prom_lacp_up_ports(dpid) == expected_up_ports:
                return
            time.sleep(1)
        self.assertEqual(self.prom_lacp_up_ports(dpid), expected_up_ports)

    def require_linux_bond_up(self, host_id):
        """Checks to see if the host has properly formed into a bonded state"""
        synced_state_list = self.get_expected_synced_states(host_id)
        host = self.host_information[host_id]['host']
        bond_name = self.host_information[host_id]['bond']
        for _ in range(self.LACP_TIMEOUT * 2):
            result = host.cmd('cat /proc/net/bonding/%s|sed "s/[ \t]*$//g"' %
                              bond_name)
            result = '\n'.join([line.rstrip() for line in result.splitlines()])
            with open(os.path.join(self.tmpdir, 'bonding-state.txt'),
                      'w') as state_file:
                state_file.write(result)
            matched_all = True
            for state_txt in synced_state_list:
                if not re.search(state_txt, result):
                    matched_all = False
                    break
            if matched_all:
                return
            time.sleep(1)
        synced_state_txt = r""""""
        for state_txt in synced_state_list:
            synced_state_txt += state_txt + "\n\n"
        synced_state_txt.strip()
        self.assertFalse(
            re.search(synced_state_txt, result),
            msg='LACP did not synchronize: %s\n\nexpected:\n\n%s' %
            (result, synced_state_txt))

    def verify_lag_connectivity(self, host_id):
        """Verify LAG connectivity"""
        lacp_ports = self.host_information[host_id]['ports']
        # All ports down
        for dpid, ports in lacp_ports.items():
            for port in ports:
                self.set_port_down(port, dpid)
            self.verify_num_lag_up_ports(0, dpid)
        # Pick a port to set up
        up_dpid = random.choice(list(lacp_ports.keys()))
        up_port = random.choice(lacp_ports[up_dpid])
        self.set_port_up(up_port, up_dpid)
        self.verify_num_lag_up_ports(1, up_dpid)
        # Ensure connectivity with one port
        self.verify_lag_host_connectivity()
        # Set the other ports to UP
        for dpid, ports in lacp_ports.items():
            for port in ports:
                self.set_port_up(port, dpid)
            self.verify_num_lag_up_ports(len(ports), dpid)
        # Ensure connectivity with all ports
        self.require_linux_bond_up(host_id)
        self.verify_lag_host_connectivity()
        # Tear down first port
        self.set_port_down(up_port, up_dpid)
        self.verify_num_lag_up_ports(len(lacp_ports[up_dpid]) - 1, up_dpid)
        # Ensure connectivity with new ports only
        self.verify_lag_host_connectivity()

    def verify_lag_host_connectivity(self):
        """Verify LAG hosts can connect to any other host using the interface"""
        # Find all LACP hosts
        for lacp_id, host_options in self.host_options.items():
            if 'lacp' in host_options:
                # Found LACP host
                for dst_id in self.host_information:
                    if lacp_id == dst_id:
                        continue
                    # Test connectivity to any other host (might be another LAG host)
                    self.check_host_connectivity_by_id(lacp_id, dst_id)
예제 #19
0
 def test(self):
     """Test fault-tolerance of the topology"""
     dp_links = FaucetTopoGenerator.dp_links_networkx_graph(
         func_graph, n_dp_links=n_dp_links)
     self.set_up(num_dps, num_vlans, dp_links, stack_roots)
     self.network_function()