aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Cowie <brad@wand.net.nz>2020-05-11 16:47:09 +1200
committerGitHub <noreply@github.com>2020-05-11 16:47:09 +1200
commit34b3c7ee69c6c205f8b356357eac48e9324ef22e (patch)
tree0fe64bdc275672b8794e8dd189f6f5b438636ca2
parent719b01cc97fcfefacaa04bb85da393b251b6e3ce (diff)
parent05e7a74925ccd1d800f555d57c46a6a34d799612 (diff)
downloadfaucet-34b3c7ee69c6c205f8b356357eac48e9324ef22e.tar.gz
faucet-34b3c7ee69c6c205f8b356357eac48e9324ef22e.tar.bz2
faucet-34b3c7ee69c6c205f8b356357eac48e9324ef22e.zip
Merge pull request #3556 from mab68/topo
Test suite config & topology generation
-rw-r--r--clib/config_generator.py533
-rw-r--r--clib/mininet_test_base_topo.py508
-rw-r--r--clib/mininet_test_topo.py54
-rw-r--r--clib/mininet_test_topo_generator.py253
-rw-r--r--clib/mininet_test_watcher.py16
-rw-r--r--tests/integration/mininet_multidp_tests.py922
-rwxr-xr-xtests/tolerance/mininet_main.py11
-rw-r--r--tests/tolerance/mininet_tests.py139
-rwxr-xr-xtests/unit/clib/test_topo.py301
9 files changed, 1591 insertions, 1146 deletions
diff --git a/clib/config_generator.py b/clib/config_generator.py
new file mode 100644
index 00000000..cb68095d
--- /dev/null
+++ b/clib/config_generator.py
@@ -0,0 +1,533 @@
+
+"""Mininet Topo class with YAML config generator"""
+
+# Copyright (C) 2015 Research and Innovation Advanced Network New Zealand Ltd.
+# Copyright (C) 2015--2019 The Contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import string
+import random
+import yaml
+
+from mininet.log import output
+from mininet.topo import Topo
+
+from clib import mininet_test_util
+from clib.mininet_test_topo import FaucetHost, VLANHost, FaucetSwitch, NoControllerFaucetSwitch
+
+
+class GenerationError(Exception):
+ """Indicates a problem with generating the configuration file"""
+
+
+class FaucetTopoGenerator(Topo):
+ """Creates a mininet topology and then provides a method to generate a YAML config file"""
+
+ # Host CPU option
+ CPUF = 0.5
+ # Link delay option
+ DELAY = '1ms'
+
+ # Switch index map to switch name
+ switches_by_id = {}
+ # Switch index map to switch dpid
+ dpids_by_id = {}
+ # Host index map to host name
+ hosts_by_id = {}
+
+ # Generated hardware switch name
+ hw_name = None
+ # DPID of the hardware switch
+ hw_dpid = None
+ # List of port order for the hardware switch
+ hw_ports = None
+
+ # Function to resolve serial numbers
+ get_serialno = None
+
+ # Additional mininet host options
+ host_options = None
+
+ # The generated starting port for each switch
+ start_port = None
+ # The port order for each switch
+ port_order = None
+
+ def get_dpids(self):
+ """Returns list of DPIDs in switch index order"""
+ return [self.dpids_by_id[key] for key in sorted(self.dpids_by_id)]
+
+ def _create_link_port_map(self):
+ """Switch pair link map to list of ports for that pair"""
+ port_maps = {}
+ for i, name in self.switches_by_id.items():
+ for port, link in self.ports[name].items():
+ if self.isSwitch(link[0]):
+ peer_id = self.nodeInfo(link[0])['switch_n']
+ port_maps.setdefault((i, peer_id), [])
+ port_maps[(i, peer_id)].append(port)
+ return port_maps
+
+ def _create_host_port_map(self):
+ """Host map to linked switches to list of ports from switch to host"""
+ host_port_map = {}
+ for host, name in self.hosts_by_id.items():
+ host_port_map.setdefault(host, {})
+ for link in self.ports[name].values():
+ switch_id = self.nodeInfo(link[0])['switch_n']
+ host_port_map[host].setdefault(switch_id, [])
+ host_port_map[host][switch_id].append(link[1])
+ return host_port_map
+
+ def _create_port_map(self):
+ """Create a map from port to the true port"""
+ port_maps = {}
+ for i, dpid in self.dpids_by_id.items():
+ switch_name = self.switches_by_id[i]
+ ports = self.ports[switch_name].keys()
+ port_maps[dpid] = {'port_%d' % i: port for i, port in enumerate(ports)}
+ return port_maps
+
+ def create_port_maps(self):
+ """Return host port maps and link port maps"""
+ return self._create_port_map(), self._create_host_port_map(), self._create_link_port_map()
+
+ def dp_dpid(self, i):
+ """DP DPID"""
+ if i == 0 and self.hw_dpid:
+ return self.hw_dpid
+ reserved_range = 100
+ while True:
+ dpid = random.randint(1, (2**32 - reserved_range)) + reserved_range
+ if dpid not in self.dpids_by_id.values():
+ return str(dpid)
+
+ def vlan_name(self, i):
+ """VLAN name"""
+ return 'vlan-%i' % (i+1)
+
+ def vlan_vid(self, i):
+ """VLAN VID value"""
+ return (i+1) * 100
+
+ def router_name(self, i):
+ """Router name"""
+ return 'router-%s' % (i+1)
+
+ def __init__(self, *args, **kwargs):
+ self.switches_by_id = {}
+ self.dpids_by_id = {}
+ self.hosts_by_id = {}
+ super().__init__(*args, **kwargs)
+
+ @staticmethod
+ def _get_sid_prefix(ports_served):
+ """Return a unique switch/host prefix for a test."""
+ # Linux tools require short interface names.
+ id_chars = ''.join(sorted(string.ascii_letters + string.digits)) # pytype: disable=module-attr
+ id_a = int(ports_served / len(id_chars))
+ id_b = ports_served - (id_a * len(id_chars))
+ return '%s%s' % (
+ id_chars[id_a], id_chars[id_b])
+
+ @staticmethod
+ def extend_port_order(port_order=None, max_length=16):
+ """
+ Extends the pattern of port_port order up to max_length
+
+ Args:
+ port_order (list): List of integers in an order to extend
+ max_length (int): Maximum length to extend the list to
+ """
+ if not port_order:
+ return list(range(max_length + 1))
+ if len(port_order) >= max_length:
+ return port_order
+ extend_order = []
+ start_port = max(port_order) + 1
+ for i in port_order:
+ extend_order.append(start_port + i)
+ if len(port_order) + len(extend_order) >= max_length:
+ break
+ return port_order + extend_order
+
+ def _generate_sid_prefix(self):
+ """Returns a sid prefix for a node in the topology"""
+ return self._get_sid_prefix(self.get_serialno(self.ports_sock, self.test_name))
+
+ def _create_next_port(self, switch_name):
+ """
+ Creates and returns the next port number for a switch
+
+ Args:
+ switch_name (str): The name of the switch to generate the next port
+ """
+ index = 0
+ if switch_name in self.ports:
+ index = len(self.ports[switch_name])
+ if self.hw_name and switch_name == self.hw_name and self.hw_ports:
+ return self.hw_ports[self.port_order[index]]
+ return self.start_port + self.port_order[index]
+
+ def _add_host(self, host_index, vlans):
+ """
+ Adds a untagged/tagged host to the topology
+
+ Args:
+ sid_prefix (str): SID prefix to generate the host name
+ host_index (int): Host index to generate the host name
+ vlans (list/None/int): Type of host/vlans the host belongs to
+ """
+ sid_prefix = self._generate_sid_prefix()
+ host_opts = self.host_options.get(host_index, {})
+ host_name = None
+ if 'cls' in host_opts:
+ host_name = 'e%s%1.1u' % (sid_prefix, host_index + 1)
+ else:
+ if isinstance(vlans, int):
+ host_name = 'u%s%1.1u' % (sid_prefix, host_index + 1)
+ host_opts['cls'] = FaucetHost
+ elif isinstance(vlans, list):
+ host_name = 't%s%1.1u' % (sid_prefix, host_index + 1)
+ host_opts['vlans'] = [self.vlan_vid(vlan) for vlan in vlans]
+ host_opts['cls'] = VLANHost
+ else:
+ raise GenerationError('Unknown host type')
+ self.hosts_by_id[host_index] = host_name
+ return self.addHost(
+ cpu=self.CPUF,
+ host_n=host_index,
+ name=host_name,
+ config_vlans=vlans,
+ **host_opts)
+
+ def _add_faucet_switch(self, switch_index):
+ """
+ Adds a Faucet switch to the topology
+
+ Args:
+ sid_prefix (str): SID prefix to generate the switch name
+ switch_index (int): Switch index to generate the host name
+ dpid (int): Switch DP ID
+ """
+ sid_prefix = self._generate_sid_prefix()
+ switch_cls = FaucetSwitch
+ switch_name = 's%s' % sid_prefix
+ if switch_index == 0 and self.hw_dpid:
+ self.hw_name = switch_name
+ self.dpids_by_id[switch_index] = self.hw_dpid
+ dpid = str(int(self.hw_dpid) + 1)
+ output('bridging hardware switch DPID %s (%x) dataplane via OVS DPID %s (%x)\n' % (
+ self.hw_dpid, int(self.hw_dpid), dpid, int(dpid)))
+ switch_cls = NoControllerFaucetSwitch
+ else:
+ dpid = self.dp_dpid(switch_index)
+ self.dpids_by_id[switch_index] = dpid
+ self.switches_by_id[switch_index] = switch_name
+ return self.addSwitch(
+ name=switch_name,
+ cls=switch_cls,
+ datapath=self.ovs_type,
+ dpid=mininet_test_util.mininet_dpid(dpid),
+ switch_n=switch_index)
+
+ def _add_link(self, node, peer_node, vlans):
+ """
+ Creates and adds a link between two nodes to the topology
+
+ Args:
+ node (str): Name of the node for the link, NOTE: should ALWAYS be a switch
+ peer_node (str): Name of the peer node for the link
+ vlans (list/None/int): Type of the link
+ """
+ port1, port2 = None, None
+ opts = {}
+ if self.isSwitch(node):
+ # Node is a switch, create port
+ port1 = self._create_next_port(node)
+ if self.isSwitch(peer_node):
+ # Peer node is a switch, create port
+ port2 = self._create_next_port(peer_node)
+ else:
+ # Peer node is a host, use delay & htb options
+ opts['delay'] = self.DELAY
+ opts['use_htb'] = True
+ return self.addLink(
+ node,
+ peer_node,
+ port1=port1,
+ port2=port2,
+ **opts,
+ config_vlans=vlans)
+
+ def add_switch_topology(self, switch_links, link_vlans):
+ """
+ Adds the switches and switch-switch links to the network topology
+ Tagged links are mapped to a list of vlan indices whereas untagged links
+ are mapped to a single vlan index, stack links are mapped to None
+
+ Args:
+ switch_topology (list): List of link tuples of switch indices (u, v)
+ link_vlans (dict): Link tuple of switch indices (u, v) mapping to vlans
+ """
+ for u_id, v_id in switch_links:
+ if u_id not in self.switches_by_id:
+ self._add_faucet_switch(u_id)
+ if v_id not in self.switches_by_id:
+ self._add_faucet_switch(v_id)
+ u_name = self.switches_by_id[u_id]
+ v_name = self.switches_by_id[v_id]
+ self._add_link(u_name, v_name, link_vlans[(u_id, v_id)])
+
+ def add_host_topology(self, host_links, host_vlans):
+ """
+ Adds the hosts and host-switch links to the network topology
+ Tagged hosts are mapped to a list of vlan indices whereas untagged hosts
+ are mapped to a single vlan index
+
+ Args:
+ host_links (dict): Host index key to list of dp indices
+ host_vlans (dict): Host index key to vlan index/indices
+ """
+ for h_id, links in host_links.items():
+ vlans = host_vlans[h_id]
+ if h_id not in self.hosts_by_id:
+ self._add_host(h_id, vlans)
+ host_name = self.hosts_by_id[h_id]
+ for dp_i in links:
+ if dp_i not in self.switches_by_id:
+ self._add_faucet_switch(dp_i)
+ switch_name = self.switches_by_id[dp_i]
+ self._add_link(switch_name, host_name, vlans)
+
+ def build(self, ovs_type, ports_sock, test_name,
+ host_links, host_vlans, switch_links, link_vlans,
+ hw_dpid=None, hw_ports=None,
+ port_order=None, start_port=5,
+ get_serialno=mininet_test_util.get_serialno, host_options=None):
+ """
+ Creates a Faucet mininet topology
+
+ Args:
+ ovs_type (str): The OVS switch type
+ ports_sock (str): Port socket
+ test_name (str): Name of the test creating the mininet topology
+ host_links (dict): Host index key to list of dp indices
+ host_vlans (dict): Host index key to vlan index/indices
+ switch_links (list): List of link tuples of switch indices (u, v)
+ link_vlans (dict): Link tuple of switch indices (u, v) mapping to vlans
+ hw_dpid (int): DP ID of the hardware switch to connect to the topology
+ hw_ports (list): Map of the OVS bridge port index to hardware port number
+ port_order (list): List of integers in order for a switch port index order
+ start_port (int): The minimum start port number for all switch port numbers
+ get_serialno (func): Function to get the serial no.
+ host_options (dict): Host index map to additional mininet host options
+ """
+ # Additional test generation information
+ self.ovs_type = ovs_type
+ self.ports_sock = ports_sock
+ self.test_name = test_name
+ self.get_serialno = get_serialno
+
+ # Information for hardware switches
+ self.hw_dpid = hw_dpid
+ self.hw_ports = sorted(hw_ports) if hw_ports else []
+
+ # Additional information for special hosts
+ self.host_options = host_options if host_options else {}
+
+ # Generate a port order for all of the switches to use
+ max_ports = len(switch_links) + len(host_links)
+ self.start_port = start_port
+ self.port_order = self.extend_port_order(port_order, max_ports)
+
+ # Build the network topology
+ self.add_switch_topology(switch_links, link_vlans)
+ self.add_host_topology(host_links, host_vlans)
+
+ def get_acls_config(self, acl_options):
+ """Return the ACLs in dictionary format for the configuration file"""
+ return acl_options.copy()
+
+ def get_dps_config(self, dp_options, host_options, link_options):
+ """Return the DPs in dictionary format for the configuration file"""
+ dps_config = {}
+
+ def get_interface_config(link_name, src_port, dst_node, dst_port, vlans, options):
+ interface_config = {}
+ type_ = 'switch-switch' if dst_port else 'switch-host'
+ if isinstance(vlans, int):
+ # Untagged link
+ interface_config = {
+ 'name': 'b%u' % src_port,
+ 'description': 'untagged %s' % link_name,
+ 'native_vlan': self.vlan_name(vlans)
+ }
+ elif isinstance(vlans, list):
+ # Tagged link
+ interface_config = {
+ 'name': 'b%u' % src_port,
+ 'description': 'tagged %s' % link_name,
+ 'tagged_vlans': [self.vlan_name(vlan) for vlan in vlans]
+ }
+ elif dst_node and dst_port and vlans is None:
+ # Stack link
+ interface_config = {
+ 'name': 'b%u' % src_port,
+ 'description': 'stack %s' % link_name,
+ 'stack': {
+ 'dp': dst_node,
+ 'port': dst_port
+ }
+ }
+ else:
+ raise GenerationError('Unknown %s link type %s' % (type_, vlans))
+ if options:
+ for option_key, option_value in options.items():
+ interface_config[option_key] = option_value
+ return interface_config
+
+ def add_dp_config(src_node, dst_node, link_key, link_info, reverse=False):
+ dp_config = dps_config[src_node]
+ src_info, dst_info = self.nodeInfo(src_node), self.nodeInfo(dst_node)
+ vlans = link_info['config_vlans']
+ src_id = src_info['switch_n']
+ dp_config.setdefault('interfaces', {})
+ options = {}
+ if self.isSwitch(dst_node):
+ # Generate switch-switch config link
+ if reverse:
+ src_port, dst_port = link_info['port2'], link_info['port1']
+ else:
+ src_port, dst_port = link_info['port1'], link_info['port2']
+ link_name = 'link #%s to %s:%s' % (link_key, dst_node, dst_port)
+ options = {}
+ dst_id = dst_info['switch_n']
+ if link_options and (src_id, dst_id) in link_options:
+ options.update(link_options[(src_id, dst_id)])
+ else:
+ # Generate host-switch config link
+ src_port, dst_port = link_info['port1'], None
+ link_name = 'link #%s to %s' % (link_key, dst_node)
+ host_n = dst_info['host_n']
+ if host_options and host_n in host_options:
+ options = host_options[host_n]
+ dp_config['interfaces'].setdefault( # pytype: disable=attribute-error
+ src_port,
+ get_interface_config(link_name, src_port, dst_node, dst_port, vlans, options))
+
+ for links in self.links(withKeys=True, withInfo=True):
+ src_node, dst_node, link_key, link_info = links
+ src_info = self.nodeInfo(src_node)
+ dst_info = self.nodeInfo(dst_node)
+ if self.isSwitch(src_node):
+ dps_config.setdefault(src_node, {})
+ src_dpid = self.dpids_by_id[src_info['switch_n']]
+ dps_config[src_node].setdefault('dp_id', int(src_dpid))
+ add_dp_config(src_node, dst_node, link_key, link_info)
+ if self.isSwitch(dst_node):
+ dps_config.setdefault(dst_node, {})
+ dst_dpid = self.dpids_by_id[dst_info['switch_n']]
+ dps_config[dst_node].setdefault('dp_id', int(dst_dpid))
+ add_dp_config(dst_node, src_node, link_key, link_info, True)
+ if dp_options:
+ for dp, options in dp_options.items():
+ switch_name = self.switches_by_id[dp]
+ dps_config.setdefault(switch_name, {})
+ for option_key, option_value in options.items():
+ dps_config[switch_name][option_key] = option_value
+ return dps_config
+
+ def get_vlans_config(self, n_vlans, vlan_options):
+ """
+ Return the VLANs in dictionary format for the YAML configuration file
+
+ Args:
+ n_vlans (int): Number of VLANs to generate
+ vlan_options (dict): Additional options for each VLAN, keyed by vlan index
+ """
+ vlans_config = {}
+ for vlan in range(n_vlans):
+ vlan_name = self.vlan_name(vlan)
+ vlans_config[vlan_name] = {
+ 'vid': self.vlan_vid(vlan)
+ }
+ if vlan_options:
+ for vlan, options in vlan_options.items():
+ vlan_name = self.vlan_name(vlan)
+ for option_key, option_value in options.items():
+ vlans_config[vlan_name][option_key] = option_value
+ return vlans_config
+
+ def get_routers_config(self, routers, router_options):
+ """
+ Return the routers in dictionary format for the configuration file
+
+ Args:
+ routers (dict): Router index to list of VLANs in the router
+ router_options (dict): Additional options for each router, keyed by router index
+ """
+ routers_config = {}
+ for router, vlans in routers.items():
+ routers_config[self.router_name(router)] = {
+ 'vlans': [self.vlan_name(vlan) for vlan in vlans]
+ }
+ if router_options:
+ for router, options in router_options.items():
+ router_name = self.router_name(router)
+ for option_key, option_value in options.items():
+ routers_config[router_name][option_key] = option_value
+ return routers_config
+
+ def get_config(self, n_vlans, acl_options=None, dp_options=None, host_options=None,
+ link_options=None, vlan_options=None, routers=None, router_options=None,
+ include=None, include_optional=None):
+ """
+ Creates a Faucet YAML configuration file using the current topology
+
+ Args:
+ n_vlans (int): Number of VLANs to generate
+ acl_options (dict): Acls in use in the Faucet configuration file
+ dp_options (dict): Additional options for each DP, keyed by DP index
+ host_options (dict): Additional options for each host, keyed by host index
+ link_options (dict): Additional options for each link, keyed by indices tuple (u, v)
+ vlan_options (dict): Additional options for each VLAN, keyed by vlan index
+ routers (dict): Router index to list of VLANs in the router
+ router_options (dict): Additional options for each router, keyed by router index
+ include (list): Files to include using the the Faucet config 'include' key
+ include_optional (list): File to include using the Faucet config 'include_optional' key
+ """
+ config = {'version': 2}
+ if include:
+ config['include'] = list(include)
+ if include_optional:
+ config['include_optional'] = list(include_optional)
+ if acl_options:
+ config['acls'] = self.get_acls_config(acl_options)
+ config['vlans'] = self.get_vlans_config(n_vlans, vlan_options)
+ if routers:
+ config['routers'] = self.get_routers_config(routers, router_options)
+ config['dps'] = self.get_dps_config(dp_options, host_options, link_options)
+ return yaml.dump(config, default_flow_style=False)
+
+
+class FaucetFakeOFTopoGenerator(FaucetTopoGenerator):
+ """Generates Faucet topologies for Unittests"""
+
+ # NOTE: For now, we dont actually create the objects for the unittests
+ # so we can leave them as they are in the FaucetTopoGenerator function
+
+ def dp_dpid(self, i):
+ """DP DPID"""
+ return '%u' % (i+1)
diff --git a/clib/mininet_test_base_topo.py b/clib/mininet_test_base_topo.py
index cd45bb91..2c8e3838 100644
--- a/clib/mininet_test_base_topo.py
+++ b/clib/mininet_test_base_topo.py
@@ -11,14 +11,12 @@ import yaml # pytype: disable=pyi-error
from clib.mininet_test_util import timeout_cmd
from clib.mininet_test_base import FaucetTestBase, IPV4_ETH
-from clib.mininet_test_topo_generator import FaucetTopoGenerator
+from clib.config_generator import FaucetTopoGenerator
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
@@ -33,67 +31,76 @@ class FaucetTopoTestBase(FaucetTestBase):
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
+ configuration_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"""
+ def _init_faucet_config(self):
+ """Initialize & normalize faucet configuration file"""
+ config_vars = {}
+ for config_var in (self.config_ports, self.port_map):
+ config_vars.update(config_var)
+ faucet_config = self.CONFIG % config_vars
+ self._write_yaml_conf(self.faucet_config_path, yaml.safe_load(faucet_config))
+
+ def _annotate_interfaces_conf(self, yaml_conf):
+ """We don't need to annotate the interfaces"""
+ return yaml_conf
+
+ def _dp_ports(self):
+ """Return ports on the first DP"""
+ return list(self.topo.ports[self.topo.switches_by_id[0]].keys())
+
+ def get_gauge_watcher_config(self):
+ """Return gauge watcher config"""
+ return """
+ port_stats:
+ dps: ['%s']
+ type: 'port_stats'
+ interval: 5
+ db: 'stats_file'
+ port_state:
+ dps: ['%s']
+ type: 'port_state'
+ interval: 5
+ db: 'state_file'
+ flow_table:
+ dps: ['%s']
+ type: 'flow_table'
+ interval: 5
+ db: 'flow_dir'
+""" % (self.topo.switches_by_id[0], self.topo.switches_by_id[0], self.topo.switches_by_id[0])
+
+ def first_switch(self):
+ """Return the first switch"""
+ return self.net.get(self.topo.switches_by_id[0])
+
+ def port_labels(self, port_no):
+ """Return regex for port label"""
+ port_name = 'b%u' % port_no
+ return {'port': port_name, 'port_description': r'.+'}
+
+ def acls(self):
+ """Defined configuration 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):
+ def faucet_mac(self, 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):
+ if isinstance(vlan_index, list):
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(
@@ -103,118 +110,101 @@ class FaucetTopoTestBase(FaucetTestBase):
"""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):
+ def build_net(self, host_links=None, host_vlans=None, switch_links=None,
+ link_vlans=None, mininet_host_options=None,
+ n_vlans=1, acl_options=None, dp_options=None, host_options=None,
+ link_options=None, vlan_options=None, routers=None, router_options=None,
+ include=None, include_optional=None):
"""
- 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
+ host_links (dict): Host index key to list of dp indices
+ host_vlans (dict): Host index key to vlan index/indices
+ switch_links (list): List of link tuples of switch indices (u, v)
+ link_vlans (dict): Link tuple of switch indices (u, v) mapping to vlans
+ mininet_host_options (dict): Host index map to additional mininet host options
+ n_vlans (int): Number of VLANs to generate
+ acl_options (dict): Acls in use in the Faucet configuration file
+ dp_options (dict): Additional options for each DP, keyed by DP index
+ host_options (dict): Additional options for each host, keyed by host index
+ link_options (dict): Additional options for each link, keyed by indices tuple (u, v)
+ vlan_options (dict): Additional options for each VLAN, keyed by vlan index
+ routers (dict): Router index to list of VLANs in the router
+ router_options (dict): Additional options for each router, keyed by router index
+ include (list): Files to include using the the Faucet config 'include' key
+ include_optional (list): File to include using the Faucet config 'include_optional' key
"""
- 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,
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
hw_dpid=self.hw_dpid,
- switch_map=self.switch_map,
+ hw_ports=self.switch_map,
port_order=self.port_order,
- start_port=self.start_port
+ start_port=self.start_port,
+ host_options=mininet_host_options
)
- self.port_maps = {dpid: self.create_port_map(dpid) for dpid in self.dpids}
+ self.dpids = self.topo.get_dpids()
+ self.dpid = self.dpids[0]
+ # host_port_maps = {host_n: {switch_n: [ports, ...], ...}, ...}
+ # link_port_maps = {(switch_n, switch_m): [ports, ...], ...}
+ self.port_maps, self.host_port_maps, self.link_port_maps = self.topo.create_port_maps()
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,
+ dpid_names = {}
+ for i in self.topo.switches_by_id:
+ dpid = self.topo.dpids_by_id[i]
+ name = self.topo.switches_by_id[i]
+ dpid_names[dpid] = name
+ self.set_dpid_names(dpid_names)
+ self.CONFIG = self.topo.get_config(
+ n_vlans,
+ acl_options=self.acls(),
dp_options=dp_options,
+ host_options=host_options,
+ link_options=link_options,
+ vlan_options=vlan_options,
routers=routers,
- host_options=host_options
+ router_options=router_options,
+ include=include,
+ include_optional=include_optional
)
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
+ self.configuration_options = {
+ 'vlan': vlan_options,
+ 'acl': acl_options,
+ 'dp': dp_options,
+ 'host': host_options,
+ 'link': link_options,
+ 'router': router_options,
+ 'host_vlans': host_vlans,
+ 'link_vlans': link_vlans
+ }
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.
+ super().start_net()
+ # Create a dictionary of host information
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]
+ host = self.net.get(host_name)
+ vlan = self.configuration_options['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.set_host_ip(host, ip_interface)
self.host_information[host_id] = {
- 'host': host_obj,
+ 'host': host,
'ip': ip_interface,
- 'mac': host_obj.MAC(),
+ 'mac': host.MAC(),
'vlan': vlan,
'bond': None,
- 'ports': {}
+ 'ports': self.host_port_maps[host_id]
}
- # 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):
@@ -226,18 +216,21 @@ class FaucetTopoTestBase(FaucetTestBase):
def setup_lacp_bonds(self):
"""Search through host options for lacp hosts and configure accordingly"""
- if not self.host_options:
+ host_options = self.configuration_options['host']
+ if not host_options:
return
bond_index = 1
- for host_id, options in self.host_options.items():
+ for host_id, options in 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 dp_i, ports in self.host_port_maps[host_id].items():
for port in ports:
- self.set_port_down(port, dpid)
+ self.set_port_down(port, self.topo.dpids_by_id[dp_i])
orig_ip = host.IP()
- lacp_switches = [self.net.switches[i] for i in self.host_links[host_id]]
+ lacp_switches = [
+ self.net.get(self.topo.switches_by_id[i])
+ for i in self.host_port_maps[host_id]]
bond_members = [
pair[0].name for switch in lacp_switches for pair in host.connectionsTo(switch)]
bond_name = 'bond%u' % (bond_index)
@@ -250,8 +243,8 @@ class FaucetTopoTestBase(FaucetTestBase):
# 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),
+ '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
@@ -260,9 +253,9 @@ class FaucetTopoTestBase(FaucetTestBase):
'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 dp_i, ports in self.host_port_maps[host_id].items():
for port in ports:
- self.set_port_up(port, dpid)
+ self.set_port_up(port, self.topo.dpids_by_id[dp_i])
def setup_intervlan_host_routes(self):
"""Configure host routes between hosts that belong on routed VLANs"""
@@ -282,162 +275,8 @@ class FaucetTopoTestBase(FaucetTestBase):
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"""
+ """Print additional information when debugging"""
try:
super(FaucetTopoTestBase, self).debug()
except Exception:
@@ -446,10 +285,9 @@ class FaucetTopoTestBase(FaucetTestBase):
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}
+ for i, name in self.topo.switches_by_id.items():
+ dpid = self.dpids[i]
+ labels = {'dp_id': '0x%x' % int(dpid), 'dp_name': name}
self.assertEqual(
0, self.scrape_prometheus_var(
var='stack_cabling_errors_total', labels=labels, default=None))
@@ -511,12 +349,13 @@ class FaucetTopoTestBase(FaucetTestBase):
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)
+ for link, ports in self.link_port_maps.items():
+ for port in ports:
+ dpid = self.topo.dpids_by_id[link[0]]
+ name = self.topo.switches_by_id[link[0]]
+ status = self.stack_port_status(dpid, name, port)
links += 1
- if status == 3: # up
+ if status == 3: # STACK_STATE_UP
links_up += 1
prop_up = links_up / links
if prop_up >= prop:
@@ -527,8 +366,15 @@ class FaucetTopoTestBase(FaucetTestBase):
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
+ stack_link = None
+ count = 0
+ for sport, link in self.topo.ports[self.topo.switches_by_id[0]].items():
+ if self.topo.isSwitch(link[0]):
+ if count == stack_offset_port:
+ stack_link = (sport, link[1])
+ break
+ count += 1
+ stack_port, remote_stack_port = stack_link # pytype: disable=attribute-error
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)
@@ -548,7 +394,13 @@ class FaucetTopoTestBase(FaucetTestBase):
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
+ switch_to_switch_links = 0
+ for link in self.topo.links():
+ src_node, dst_node = link
+ if self.topo.isSwitch(src_node):
+ if self.topo.isSwitch(dst_node):
+ switch_to_switch_links += 1
+ num_arp_expected = switch_to_switch_links * 2
tcpdump_filter = 'arp and ether src %s' % ping_host.MAC()
tcpdump_txt = self.tcpdump_helper(
tcpdump_host, tcpdump_filter, [
@@ -617,8 +469,8 @@ class FaucetTopoTestBase(FaucetTestBase):
"""
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():
+ dp_hosts = {self.topo.switches_by_id[dp_index]: ([], []) for dp_index in range(self.NUM_DPS)}
+ for host_id, options in self.configuration_options['host'].items():
host = self.host_information[host_id]['host']
if options.get('loop_protect_external', False):
ext_hosts.append(host)
@@ -626,8 +478,8 @@ class FaucetTopoTestBase(FaucetTestBase):
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)
+ for dp_i in self.host_port_maps[host_id].keys():
+ dp_hosts[self.topo.switches_by_id[dp_i]][int_or_ext].append(host)
return set(int_hosts), set(ext_hosts), dp_hosts
def verify_protected_connectivity(self):
@@ -735,7 +587,7 @@ class FaucetTopoTestBase(FaucetTestBase):
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']
+ oper_key = self.configuration_options['host'][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:
@@ -773,21 +625,20 @@ details partner lacp pdu:
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():
+ for host_id, options in self.configuration_options['host'].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
+ for dp_i, ports in self.host_port_maps[host_id].items():
+ if dpid == self.topo.dpids_by_id[dp_i]:
+ # Host has links to dpid
+ for port in 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):
@@ -826,21 +677,24 @@ details partner lacp pdu:
def verify_lag_connectivity(self, host_id):
"""Verify LAG connectivity"""
- lacp_ports = self.host_information[host_id]['ports']
+ lacp_ports = self.host_port_maps[host_id]
# All ports down
- for dpid, ports in lacp_ports.items():
+ for dp_i, ports in lacp_ports.items():
+ dpid = self.topo.dpids_by_id[dp_i]
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])
+ up_dp = random.choice(list(lacp_ports.keys()))
+ up_dpid = self.topo.dpids_by_id[up_dp]
+ up_port = random.choice(lacp_ports[up_dp])
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 dp_i, ports in lacp_ports.items():
+ dpid = self.topo.dpids_by_id[dp_i]
for port in ports:
self.set_port_up(port, dpid)
self.verify_num_lag_up_ports(len(ports), dpid)
@@ -849,14 +703,14 @@ details partner lacp pdu:
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)
+ self.verify_num_lag_up_ports(len(lacp_ports[up_dp])-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():
+ for lacp_id, host_options in self.configuration_options['host'].items():
if 'lacp' in host_options:
# Found LACP host
for dst_id in self.host_information:
diff --git a/clib/mininet_test_topo.py b/clib/mininet_test_topo.py
index 81985d65..77f3d2b7 100644
--- a/clib/mininet_test_topo.py
+++ b/clib/mininet_test_topo.py
@@ -56,6 +56,33 @@ class FaucetHost(CPULimitedHost):
"""Base Mininet Host class, for Mininet-based tests."""
+class VLANHost(FaucetHost):
+ """Implementation of a Mininet host on a tagged VLAN."""
+
+ intf_root_name = None
+
+ def config(self, vlans=[100], **params): # pylint: disable=arguments-differ
+ """Configure VLANHost according to (optional) parameters:
+ vlans (list): List of VLAN IDs for default interface"""
+ super_config = super().config(**params)
+ intf = self.defaultIntf()
+ vlan_intf_name = '%s.%s' % (intf, '.'.join(str(v) for v in vlans))
+ cmds = [
+ 'ip -4 addr flush dev %s' % intf,
+ 'ip -6 addr flush dev %s' % intf,
+ 'ip link set dev %s up' % vlan_intf_name,
+ 'ip -4 addr add %s dev %s' % (params['ip'], vlan_intf_name)
+ ]
+ for v in vlans:
+ cmds.append('vconfig add %s %d' % (intf, v))
+ for cmd in cmds:
+ self.cmd(cmd)
+ self.intf_root_name = intf.name
+ intf.name = vlan_intf_name
+ self.nameToIntf[vlan_intf_name] = intf
+ return super_config
+
+
class FaucetSwitch(OVSSwitch):
"""Switch that will be used by all tests (netdev based OVS)."""
@@ -154,33 +181,6 @@ class NoControllerFaucetSwitch(FaucetSwitch):
super().start(controllers=[])
-class VLANHost(FaucetHost):
- """Implementation of a Mininet host on a tagged VLAN."""
-
- intf_root_name = None
-
- def config(self, vlans=[100], **params): # pylint: disable=arguments-differ
- """Configure VLANHost according to (optional) parameters:
- vlans (list): List of VLAN IDs for default interface"""
- super_config = super().config(**params)
- intf = self.defaultIntf()
- vlan_intf_name = '%s.%s' % (intf, '.'.join(str(v) for v in vlans))
- cmds = [
- 'ip -4 addr flush dev %s' % intf,
- 'ip -6 addr flush dev %s' % intf,
- 'ip link set dev %s up' % vlan_intf_name,
- 'ip -4 addr add %s dev %s' % (params['ip'], vlan_intf_name)
- ]
- for v in vlans:
- cmds.append('vconfig add %s %d' % (intf, v))
- for cmd in cmds:
- self.cmd(cmd)
- self.intf_root_name = intf.name
- intf.name = vlan_intf_name
- self.nameToIntf[vlan_intf_name] = intf
- return super_config
-
-
class FaucetSwitchTopo(Topo):
"""FAUCET switch topology that contains a software switch."""
diff --git a/clib/mininet_test_topo_generator.py b/clib/mininet_test_topo_generator.py
deleted file mode 100644
index 4a35f0ba..00000000
--- a/clib/mininet_test_topo_generator.py
+++ /dev/null
@@ -1,253 +0,0 @@
-#!/usr/bin/env python3
-
-
-from clib.mininet_test_util import get_serialno
-from clib.mininet_test_topo import FaucetSwitchTopo, SWITCH_START_PORT
-
-
-class FaucetTopoGenerator(FaucetSwitchTopo):
- """
- Generate a Faucet topology for use in the mininet integration tests
- FaucetTopoGenerator is able to connect up a network in an arbitrary
- topology based off a dp_link, host_link and host_vlan dictionary
- """
-
- @staticmethod
- def dp_links_networkx_graph(graph, offset=0, n_dp_links=1):
- """
- Networkx provides methods for generating different graphs
- Args:
- graph: Networkx graph
- offset: DP offset
- n_dp_links: Redundant switch-switch links
- Return dp_links a networkx graph
- """
- dp_links = {}
- for edge in graph.edges():
- src = edge[0] + offset
- dst = edge[1] + offset
- if src not in dp_links:
- dp_links[src] = []
- for _ in range(n_dp_links):
- dp_links[src].append(dst)
- return dp_links
-
- @staticmethod
- def tagged_untagged_hosts(n_dps, n_tagged, n_untagged,
- n_host_links=1, dp_offset=0, host_offset=0):
- """
- Generate links & vlans for a number of tagged and untagged vlan hosts on each dp
- Args:
- n_dps: Number of DPs to generate hosts on
- n_tagged: Number of tagged hosts to generate on each DP
- n_untagged: Number of untagged hosts to generate on each DP
- n_host_links: Number of redundant host to switch links
- dp_offset: DP index offset
- host_offset: Host index offset
- Return host_links, host_vlans
- """
- host_links = {}
- host_vlans = {}
- vlan = 0
- host_id = host_offset
- for i in range(n_dps):
- for _ in range(n_tagged):
- host_links[host_id] = []
- for _ in range(n_host_links):
- host_links[host_id].append(i + dp_offset)
- host_vlans[host_id] = (vlan,)
- host_id += 1
- for _ in range(n_untagged):
- host_links[host_id] = []
- for _ in range(n_host_links):
- host_links[host_id].append(i + dp_offset)
- host_vlans[host_id] = vlan
- host_id += 1
- return host_links, host_vlans
-
- @staticmethod
- def tagged_vlan_hosts(n_dps, vlan, n_host_links=1, dp_offset=0, host_offset=0):
- """
- Generate dictionaries for a single tagged host on each DP
- Args:
- n_dps: Number of DPs to generate hosts on
- vlan: The host's tagged VLAN
- n_host_links: Number of redundant links
- dp_offset: DP index offset
- host_offset: Host index offset
- Return host_links, host_vlans
- """
- host_links = {}
- host_vlans = {}
- host_id = host_offset
- for i in range(n_dps):
- host_links[host_id] = []
- for _ in range(n_host_links):
- host_links[host_id].append(i + dp_offset)
- host_vlans[host_id] = (vlan, )
- host_id += 1
- return host_links, host_vlans
-
- @staticmethod
- def untagged_vlan_hosts(n_dps, n_vlans, n_host_links=1, dp_offset=0, host_offset=0):
- """
- Generate dictionaries for an untagged host on each vlan on each DP
- Args:
- n_dps: Number of DPs to generate hosts on
- n_vlans: Number of vlans to generate hosts on
- n_host_links: Number of redundant links
- dp_offset: DP index offset
- host_offset: Host index offset
- Return host_links, host_vlans
- """
- host_links = {}
- host_vlans = {}
- host_id = host_offset
- for i in range(n_dps):
- for vlan in range(n_vlans):
- host_links[host_id] = []
- for _ in range(n_host_links):
- host_links[host_id].append(i + dp_offset)
- host_vlans[host_id] = vlan
- host_id += 1
- return host_links, host_vlans
-
- @staticmethod
- def untagged_vlan_hosts_by_amount(n_dps, n_vlan_hosts,
- n_host_links=1, dp_offset=0, host_offset=0):
- """
- Generate dictionaries for untagged hosts on each DP with specified number of hosts
- Args:
- n_dps: Number of DPs to generate hosts on
- n_vlans: Number of VLANs
- n_vlan_hosts (dict): VLAN index to number of hosts on that VLAN on each DP
- n_host_links: Number of redundant host-switch links
- dp_offset: DP index offset
- host_offset: Host index offset
- Return host_links, host_vlans
- """
- host_links = {}
- host_vlans = {}
- host_id = host_offset
- for i in range(n_dps):
- for vlan, n_hosts in n_vlan_hosts.items():
- for _ in range(n_hosts):
- host_links[host_id] = []
- for _ in range(n_host_links):
- host_links[host_id].append(i + dp_offset)
- host_vlans[host_id] = vlan
- host_id += 1
- return host_links, host_vlans
-
- def dpid_peer_links(self, dpid):
- """Return peer_link list for dpid, remapping if necessary"""
- name = self.dpid_names[dpid]
- links = [self.hw_remap_peer_link(dpid, link) for link in self.switch_peer_links[name]]
- return links
-
- def _add_host_to_switch_link(self, switch, dpid, host, curr_index):
- """
- Add a link from a switch to a host
- Args:
- switch: Switch
- dpid: Switch dpid
- host: Host
- curr_index: Port order index
- """
- self.switch_ports.setdefault(switch, [])
- self.dpid_port_host.setdefault(int(dpid), {})
- index = curr_index
- port = self.start_port + self.port_order[index]
- self.addLink(switch, host, port1=port, delay=self.DELAY, use_htb=True)
- self.switch_ports[switch].append(port)
- self.dpid_port_host[int(dpid)][port] = host
- index += 1
- return index
-
- def _add_switch_to_switch_link(self, src, dst, next_index):
- """
- Args:
- src: Source switch
- dst: Dest switch
- next_index: Next port order index
- """
- self.switch_peer_links.setdefault(src, [])
- self.switch_peer_links.setdefault(dst, [])
- dpid1, dpid2 = self.switch_dpids[src], self.switch_dpids[dst]
- index1, index2 = next_index[src], next_index[dst]
- port1, port2 = [self.start_port + self.port_order[i] for i in (index1, index2)]
- self.addLink(src, dst, port1=port1, port2=port2)
- # Update port and link lists
- self.switch_ports.setdefault(src, [])
- self.switch_ports.setdefault(dst, [])
- self.switch_ports[src].append(port1)
- self.switch_ports[dst].append(port2)
- self.switch_peer_links[src].append(self.peer_link(port1, dpid2, port2))
- self.switch_peer_links[dst].append(self.peer_link(port2, dpid1, port1))
- # Update next indices on src and dest
- next_index[src] += 1
- next_index[dst] += 1
-
- def build(self, ovs_type, ports_sock, test_name, dpids,
- dp_links, host_links, host_vlans, vlan_vids,
- hw_dpid=None, switch_map=None, start_port=SWITCH_START_PORT,
- port_order=None, get_serialno=get_serialno):
- """
- Creates the Faucet mininet switches & hosts
- Args:
- dp_links (dict): dp id key to list of dp id value
- host_links (dict): host id key to list of dp id value
- host_vlans (dict): host id key to vlans id value
- vlan_vids (dict): VLAN IDs for vlan index
- """
- self.hw_dpid = hw_dpid
- self.hw_ports = sorted(switch_map) if switch_map else []
- self.start_port = start_port
-
- self.switch_to_switch_links = 0
- for dplinks in dp_links.values():
- self.switch_to_switch_links += len(dplinks)
-
- self.host_to_switch_links = 0
- for hostlinks in host_links.values():
- self.host_to_switch_links += len(hostlinks)
-
- max_ports = self.host_to_switch_links + (2 * self.switch_to_switch_links)
- self.port_order = self.extend_port_order(port_order, max_ports)
-
- # Create hosts
- self.hosts_by_id = {}
- for host_id, vlans in host_vlans.items():
- serialno = get_serialno(ports_sock, test_name)
- sid_prefix = self._get_sid_prefix(serialno)
- if isinstance(vlans, int):
- self.hosts_by_id[host_id] = self._add_untagged_host(sid_prefix, host_id)
- elif isinstance(vlans, tuple):
- self.hosts_by_id[host_id] = self._add_tagged_host(
- sid_prefix, [vlan_vids[v] for v in vlans], host_id)
-
- # Create switches & then host-switch links
- self.switch_peer_links = {}
- next_index = {}
- self.dpid_to_switch = {}
- for i, dpid in enumerate(dpids):
- serialno = get_serialno(ports_sock, test_name)
- sid_prefix = self._get_sid_prefix(serialno)
- switch = self._add_faucet_switch(sid_prefix, dpid, hw_dpid, ovs_type)
- self.dpid_to_switch[dpid] = switch
- next_index[switch] = 0
- # Create host-switch links
- for host_id, hostlinks in host_links.items():
- if i in hostlinks:
- n_links = hostlinks.count(i)
- for _ in range(n_links):
- host = self.hosts_by_id[host_id]
- next_index[switch] = self._add_host_to_switch_link(
- switch, dpid, host, next_index[switch])
-
- # Create switch-switch links
- for src_index, dplinks in dp_links.items():
- for dst_index in dplinks:
- src = self.dpid_to_switch[dpids[src_index]]
- dst = self.dpid_to_switch[dpids[dst_index]]
- self._add_switch_to_switch_link(src, dst, next_index)
diff --git a/clib/mininet_test_watcher.py b/clib/mininet_test_watcher.py
index 05ff5a06..41d9fbb0 100644
--- a/clib/mininet_test_watcher.py
+++ b/clib/mininet_test_watcher.py
@@ -28,33 +28,33 @@ class TopologyWatcher():
# Dict of src host key and dst hosts value
connected_hosts = None
- def __init__(self, dpids, dp_links, host_links, n_vlans, host_information, routers):
+ def __init__(self, dpids, switch_links, host_links, n_vlans, host_information, routers):
"""
Args:
dpids (list): Switch dpids to match the DPID indices used in dp_links & other structures
- dp_links (dict):
+ switch_links (dict):
host_links (dict):
n_vlans: Number of VLANs
host_information (dict):
"""
self.dpids = dpids
- self.dp_links = dp_links
+ self.switch_links = switch_links
self.host_links = host_links
self.n_vlans = n_vlans
self.host_information = host_information
self.routers = routers
self.fault_list = []
self.add_fault('Initial')
- self.generate_predicted_graph(dpids, dp_links, host_links, host_information)
+ self.generate_predicted_graph(dpids, switch_links, host_links, host_information)
- def generate_predicted_graph(self, dpids, dp_links, host_links, host_information):
+ def generate_predicted_graph(self, dpids, switch_links, host_links, host_information):
"""Creates the predicted network graph"""
self.predicted_network_graph = networkx.MultiGraph()
for dpid in dpids:
self.predicted_network_graph.add_node(dpid)
- for src, dsts in dp_links.items():
- for dst in dsts:
- self.predicted_network_graph.add_edge(self.dpids[src], self.dpids[dst])
+ for link in switch_links:
+ u, v = link
+ self.predicted_network_graph.add_edge(self.dpids[u], self.dpids[v])
self.host_name_to_index = {}
for host_id, host_info in host_information.items():
host_name = host_info['host'].name
diff --git a/tests/integration/mininet_multidp_tests.py b/tests/integration/mininet_multidp_tests.py
index 6bb2a387..c1a0e5c1 100644
--- a/tests/integration/mininet_multidp_tests.py
+++ b/tests/integration/mininet_multidp_tests.py
@@ -1,58 +1,192 @@
#!/usr/bin/env python3
+"""Mininet multi-switch integration tests for Faucet"""
+
import json
import os
-import time
import networkx
-import json
from mininet.log import error
from clib.mininet_test_base import IPV4_ETH, IPV6_ETH
-from clib.mininet_test_topo_generator import FaucetTopoGenerator
from clib.mininet_test_base_topo import FaucetTopoTestBase
class FaucetMultiDPTest(FaucetTopoTestBase):
- """Replaces the FaucetStringOfDPTest for the old integration tests"""
+ """Converts old FaucetStringOfDPTest class to a generalized test topology & config builder"""
+
+ def mininet_host_options(self):
+ """Additional mininet host options"""
+ return {}
+
+ def include(self):
+ """Additional include files"""
+ return []
+
+ def include_optional(self):
+ """Additional optional-include files"""
+ return []
+
+ def dp_options(self):
+ """Additional DP options"""
+ return {}
+
+ def host_options(self):
+ """Additional host options"""
+ return {}
+
+ def link_options(self):
+ """Additional link options"""
+ return {}
+
+ def vlan_options(self):
+ """Additional VLAN options"""
+ return {}
+
+ def router_options(self):
+ """Additional router options"""
+ return {}
+
+ def link_acls(self):
+ """Host index or (switch index, switch index) link to acls_in mapping"""
+ return {}
def setUp(self):
pass
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_trunk=False, use_external=False,
- vlan_options=None, dp_options=None, routers=None):
- """Set up a network with the given parameters"""
- super(FaucetMultiDPTest, self).setUp()
+ switch_to_switch_links=1, stack_ring=False,
+ lacp_trunk=False, use_external=False, routers=None):
+ """
+ Args:
+ stack (bool): Whether to use stack or trunk links
+ n_dps (int): The number of DPs in the topology
+ n_tagged (int): The number of tagged hosts per DP
+ n_untagged (int): The number of untagged hosts per DP
+ switch_to_switch_links (int): The number of switch-switch links to generate
+ stack_ring (bool): Whether to generate a cycle graph or a path graph
+ lacp_trunk (bool): If true, configure LACP on trunk ports
+ use_external (bool): If true, configure loop_protect_external
+ routers (dict): The routers to generate in the configuration file
+ """
+ super().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)
+ dp_links = networkx.cycle_graph(n_dps)
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)
+ dp_links = networkx.path_graph(n_dps)
+ # Create list of switch-switch links for network topology
+ switch_links = []
+ switch_links = list(dp_links.edges()) * switch_to_switch_links
+ # Create link type for the switch-switch links
+ link_vlans = {}
+ vlans = None if stack else list(range(n_vlans))
+ for dp_i in dp_links.nodes():
+ for link in dp_links.edges(dp_i):
+ link_vlans[link] = vlans
+ # Create link configuration options for DP interfaces
+ link_options = {}
+ for dp_i in dp_links.nodes():
+ for link in dp_links.edges(dp_i):
+ if lacp_trunk:
+ link_options.setdefault(link, {})
+ link_options[link] = {
+ 'lacp': 1,
+ 'lacp_active': True
+ }
+ if self.link_options():
+ for dp_i in dp_links.nodes():
+ for link in dp_links.edges(dp_i):
+ link_options.setdefault(link, {})
+ for opt_key, opt_value in self.link_options().items():
+ link_options[link][opt_key] = opt_value
+ # Create host link topology and vlan information
+ host_links = {}
+ host_vlans = {}
+ host = 0
+ for dp_i in range(n_dps):
+ for _ in range(n_tagged):
+ host_links[host] = [dp_i]
+ vlans = list(range(n_vlans))
+ host_vlans[host] = vlans
+ host += 1
+ for _ in range(n_untagged):
+ host_links[host] = [dp_i]
+ host_vlans[host] = 0
+ host += 1
+ # Create Host configuration options for DP interfaces
host_options = {}
- values = [False for _ in range(n_dps)]
if use_external:
- for host_id, links in host_links.items():
+ # The first host with a link to a switch without an external host
+ # becomes an external host on all links
+ values = [False for _ in range(n_dps)]
+ for host, links in host_links.items():
+ make_external = False
for link in links:
- host_options[host_id] = {'loop_protect_external': values[link]}
- values[link] = True
+ if not values[link]:
+ make_external = True
+ values[link] = True
+ host_options.setdefault(host, {})
+ host_options[host]['loop_protect_external'] = make_external
+ for host in host_links:
+ for h_key, h_value in self.host_options().items():
+ host_options[host][h_key] = h_value
+ # Create DP configuration options
+ dp_options = {}
+ for dp_i in range(n_dps):
+ dp_options.setdefault(dp_i, {
+ 'group_table': self.GROUP_TABLE,
+ 'ofchannel_log': self.debug_log_path + str(dp_i) if self.debug_log_path else None,
+ 'hardware': self.hardware if dp_i == 0 and self.hw_dpid else 'Open vSwitch'
+ })
+ if stack and dp_i == 0:
+ dp_options[dp_i]['stack'] = {'priority': 1}
+ if lacp_trunk:
+ dp_options[dp_i]['lacp_timeout'] = 10
+ for dp_key, dp_value in self.dp_options().items():
+ dp_options[dp_i][dp_key] = dp_value
+ # Create VLAN configuration options
+ vlan_options = {}
+ if routers:
+ for vlans in self.routers():
+ for vlan in vlans:
+ if vlan not in vlan_options:
+ vlan_options[vlan] = {
+ 'faucet_mac': self.faucet_mac(vlan),
+ 'faucet_vips': [self.faucet_vip(vlan)],
+ 'targeted_gw_resolution': False
+ }
+ for vlan in range(n_vlans):
+ vlan_options.setdefault(vlan, {})
+ for vlan_key, vlan_value in self.vlan_options().items():
+ vlan_options[vlan][vlan_key] = vlan_value
+ if self.link_acls():
+ for link, acls in self.link_acls().items():
+ if isinstance(link, tuple):
+ # link ACL
+ link_options.setdefault(link, {})
+ link_options[link]['acls_in'] = acls
+ elif isinstance(link, int):
+ # host ACL
+ host_options.setdefault(link, {})
+ host_options[link]['acls_in'] = acls
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_trunk=lacp_trunk, host_options=host_options)
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
+ mininet_host_options=self.mininet_host_options(),
+ n_vlans=n_vlans,
+ dp_options=dp_options,
+ host_options=host_options,
+ link_options=link_options,
+ vlan_options=vlan_options,
+ routers=routers,
+ router_options=self.router_options(),
+ include=self.include(),
+ include_optional=self.include_optional()
+ )
self.start_net()
@@ -102,6 +236,7 @@ class FaucetSingleStackStringOfDPTagged1Test(FaucetMultiDPTest):
NUM_DPS = 3
def test_tagged(self):
+ """Test all tagged hosts in stack topology can reach each other with one stack down"""
self.set_up(
stack=True, n_dps=self.NUM_DPS, n_tagged=self.NUM_HOSTS, switch_to_switch_links=2)
self.verify_stack_up()
@@ -115,27 +250,39 @@ class FaucetStringOfDPLACPUntaggedTest(FaucetMultiDPTest):
NUM_DPS = 2
NUM_HOSTS = 2
SOFTWARE_ONLY = True
- match_bcast = {'dl_vlan': FaucetMultiDPTest.vlan_vid(0), 'dl_dst': 'ff:ff:ff:ff:ff:ff'}
+ match_bcast = {'dl_vlan': 100, 'dl_dst': 'ff:ff:ff:ff:ff:ff'}
action_str = 'OUTPUT:%u'
def setUp(self): # pylint: disable=invalid-name
+ """Setup network & create config file"""
super(FaucetStringOfDPLACPUntaggedTest, self).set_up(
stack=False,
n_dps=self.NUM_DPS,
n_untagged=self.NUM_HOSTS,
switch_to_switch_links=2,
- hw_dpid=self.hw_dpid,
lacp_trunk=True)
def lacp_ports(self):
"""Return LACP ports"""
- first_link, second_link = sorted(self.non_host_links(self.dpid))
- first_lacp_port, second_lacp_port = first_link.port, second_link.port
- remote_first_lacp_port, remote_second_lacp_port = first_link.peer_port, second_link.peer_port
+ # We sort non_host_links by port because FAUCET sorts its ports
+ # and only floods out of the first active LACP port in that list
+ sname = self.topo.switches_by_id[0]
+ dname = self.topo.switches_by_id[1]
+ first_link, second_link = None, None
+ for sport, link in self.topo.ports[sname].items():
+ if link[0] == dname:
+ if first_link is None:
+ first_link = (sport, link[1])
+ else:
+ second_link = (sport, link[1])
+ first_link, second_link = sorted([first_link, second_link])
+ first_lacp_port, remote_first_lacp_port = first_link
+ second_lacp_port, remote_second_lacp_port = second_link
return (first_lacp_port, second_lacp_port,
remote_first_lacp_port, remote_second_lacp_port)
def wait_for_lacp_state(self, port_no, wanted_state, dpid, dp_name, timeout=30):
+ """Wait for LACP port state"""
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(
@@ -159,14 +306,11 @@ class FaucetStringOfDPLACPUntaggedTest(FaucetMultiDPTest):
"""Wait for LACP state NOSYNC"""
self.wait_for_lacp_state(port_no, 5, dpid, dp_name)
- # We sort non_host_links by port because FAUCET sorts its ports
- # and only floods out of the first active LACP port in that list
-
def wait_for_all_lacp_up(self):
"""Wait for all LACP ports to be up"""
(first_lacp_port, second_lacp_port, remote_first_lacp_port, _) = self.lacp_ports()
- self.wait_for_lacp_port_up(first_lacp_port, self.dpid, self.DP_NAME)
- self.wait_for_lacp_port_up(second_lacp_port, self.dpid, self.DP_NAME)
+ self.wait_for_lacp_port_up(first_lacp_port, self.dpids[0], self.topo.switches_by_id[0])
+ self.wait_for_lacp_port_up(second_lacp_port, self.dpids[0], self.topo.switches_by_id[0])
self.wait_until_matching_flow(
self.match_bcast, self._FLOOD_TABLE, actions=[self.action_str % first_lacp_port])
self.wait_until_matching_flow(
@@ -190,9 +334,9 @@ class FaucetStringOfDPLACPUntaggedTest(FaucetMultiDPTest):
other_remote_lacp_port = list(remote_ports - {remote_lacp_port})[0]
self.set_port_down(local_lacp_port, wait=False)
self.wait_for_lacp_port_none(
- local_lacp_port, self.dpid, self.DP_NAME)
+ local_lacp_port, self.dpids[0], self.topo.switches_by_id[0])
self.wait_for_lacp_port_none(
- remote_lacp_port, self.dpids[1], 'faucet-2')
+ remote_lacp_port, self.dpids[1], self.topo.switches_by_id[1])
self.wait_until_matching_flow(
self.match_bcast, self._FLOOD_TABLE, actions=[
self.action_str % other_local_lacp_port])
@@ -218,9 +362,11 @@ class FaucetStringOfDPLACPUntaggedTest(FaucetMultiDPTest):
event = json.loads(event_log_line.strip())
if 'LAG_CHANGE' in event:
lag_event_found = event.get('LAG_CHANGE')
+ break
self.assertTrue(lag_event_found)
if lag_event_found:
- self.assertTrue(lag_event_found.get('state') and lag_event_found.get('role'))
+ self.assertIn('state', lag_event_found)
+ self.assertIn('role', lag_event_found)
def test_dyn_fail(self):
"""Test lacp fail on reload with dynamic lacp status."""
@@ -228,17 +374,17 @@ class FaucetStringOfDPLACPUntaggedTest(FaucetMultiDPTest):
conf = self._get_faucet_conf()
(src_port, dst_port, fail_port, _) = self.lacp_ports()
- self.wait_for_lacp_port_up(src_port, self.dpids[0], 'faucet-1')
- self.wait_for_lacp_port_up(dst_port, self.dpids[0], 'faucet-1')
+ self.wait_for_lacp_port_up(src_port, self.dpids[0], self.topo.switches_by_id[0])
+ self.wait_for_lacp_port_up(dst_port, self.dpids[0], self.topo.switches_by_id[0])
- interfaces_conf = conf['dps']['faucet-2']['interfaces']
+ interfaces_conf = conf['dps'][self.topo.switches_by_id[1]]['interfaces']
interfaces_conf[fail_port]['lacp'] = 0
interfaces_conf[fail_port]['lacp_active'] = False
self.reload_conf(conf, self.faucet_config_path, restart=True,
cold_start=False, change_expected=False)
- self.wait_for_lacp_port_init(src_port, self.dpids[0], 'faucet-1')
- self.wait_for_lacp_port_up(dst_port, self.dpids[0], 'faucet-1')
+ self.wait_for_lacp_port_init(src_port, self.dpids[0], self.topo.switches_by_id[0])
+ self.wait_for_lacp_port_up(dst_port, self.dpids[0], self.topo.switches_by_id[0])
def test_passthrough(self):
"""Test lacp passthrough on port fail."""
@@ -246,12 +392,12 @@ class FaucetStringOfDPLACPUntaggedTest(FaucetMultiDPTest):
conf = self._get_faucet_conf()
(src_port, dst_port, fail_port, end_port) = self.lacp_ports()
- interfaces_conf = conf['dps']['faucet-1']['interfaces']
+ interfaces_conf = conf['dps'][self.topo.switches_by_id[0]]['interfaces']
interfaces_conf[dst_port]['lacp_passthrough'] = [src_port]
interfaces_conf[dst_port]['loop_protect_external'] = True
interfaces_conf[dst_port]['lacp'] = 2
interfaces_conf[src_port]['loop_protect_external'] = True
- interfaces_conf = conf['dps']['faucet-2']['interfaces']
+ interfaces_conf = conf['dps'][self.topo.switches_by_id[1]]['interfaces']
interfaces_conf[fail_port]['loop_protect_external'] = True
interfaces_conf[end_port]['loop_protect_external'] = True
interfaces_conf[end_port]['lacp'] = 2
@@ -267,9 +413,9 @@ class FaucetStringOfDPLACPUntaggedTest(FaucetMultiDPTest):
self.reload_conf(conf, self.faucet_config_path, restart=True,
cold_start=False, change_expected=False)
- self.wait_for_lacp_port_init(src_port, self.dpids[0], 'faucet-1')
- self.wait_for_lacp_port_up(dst_port, self.dpids[0], 'faucet-1')
- self.wait_for_lacp_port_init(end_port, self.dpids[1], 'faucet-2')
+ self.wait_for_lacp_port_init(src_port, self.dpids[0], self.topo.switches_by_id[0])
+ self.wait_for_lacp_port_up(dst_port, self.dpids[0], self.topo.switches_by_id[0])
+ self.wait_for_lacp_port_init(end_port, self.dpids[1], self.topo.switches_by_id[1])
class FaucetStackStringOfDPUntaggedTest(FaucetMultiDPTest):
@@ -280,6 +426,7 @@ class FaucetStackStringOfDPUntaggedTest(FaucetMultiDPTest):
SOFTWARE_ONLY = True
def verify_events_log(self, event_log):
+ """Verify event log has correct L2 learn events"""
with open(event_log, 'r') as event_log_file:
events = [json.loads(event_log_line.strip()) for event_log_line in event_log_file]
l2_learns = [event['L2_LEARN'] for event in events if 'L2_LEARN' in event]
@@ -292,7 +439,7 @@ class FaucetStackStringOfDPUntaggedTest(FaucetMultiDPTest):
"""All untagged hosts in stack topology can reach each other."""
self.set_up(
stack=True, n_dps=self.NUM_DPS, n_untagged=self.NUM_HOSTS,
- switch_to_switch_links=2, hw_dpid=self.hw_dpid)
+ switch_to_switch_links=2)
self._enable_event_log()
self.verify_stack_hosts()
self.verify_events_log(self.event_log)
@@ -305,12 +452,12 @@ class FaucetSingleStackStringOfDPExtLoopProtUntaggedTest(FaucetMultiDPTest):
NUM_HOSTS = 3
def setUp(self): # pylint: disable=invalid-name
+ """Setup network & configuration file"""
super(FaucetSingleStackStringOfDPExtLoopProtUntaggedTest, self).set_up(
stack=True,
n_dps=self.NUM_DPS,
n_untagged=self.NUM_HOSTS,
switch_to_switch_links=2,
- hw_dpid=self.hw_dpid,
use_external=True)
def test_untagged(self):
@@ -324,7 +471,8 @@ class FaucetSingleStackStringOfDPExtLoopProtUntaggedTest(FaucetMultiDPTest):
# Part 2: Test the code on pipeline reconfiguration path.
conf = self._get_faucet_conf()
loop_interface = None
- for interface, interface_conf in conf['dps'][self.dp_name(1)]['interfaces'].items():
+ dp_name = self.topo.switches_by_id[1]
+ for interface, interface_conf in conf['dps'][dp_name]['interfaces'].items():
if 'stack' in interface_conf:
continue
if not interface_conf.get('loop_protect_external', False):
@@ -337,18 +485,19 @@ class FaucetSingleStackStringOfDPExtLoopProtUntaggedTest(FaucetMultiDPTest):
# Part 3: Make sure things are the same after reload.
self.verify_protected_connectivity() # After reload
- def _mark_external(self, loop_interface, protect_external):
+ def _mark_external(self, loop_intf, protect_external):
"""Change the loop interfaces loop_protect_external option"""
conf = self._get_faucet_conf()
- conf['dps'][self.dp_name(1)]['interfaces'][loop_interface]['loop_protect_external'] = protect_external
+ dp_name = self.topo.switches_by_id[1]
+ conf['dps'][dp_name]['interfaces'][loop_intf]['loop_protect_external'] = protect_external
self.reload_conf(
conf, self.faucet_config_path,
restart=True, cold_start=False, change_expected=True)
def test_missing_ext(self):
"""Test stacked dp with all external ports down on a switch"""
- self.validate_with_externals_down_fails(self.dp_name(0))
- self.validate_with_externals_down_fails(self.dp_name(1))
+ self.validate_with_externals_down_fails(self.topo.switches_by_id[0])
+ self.validate_with_externals_down_fails(self.topo.switches_by_id[1])
class FaucetSingleStackStringOf3DPExtLoopProtUntaggedTest(FaucetMultiDPTest):
@@ -360,10 +509,10 @@ class FaucetSingleStackStringOf3DPExtLoopProtUntaggedTest(FaucetMultiDPTest):
def test_untagged(self):
"""Test the external loop protect with stacked DPs and untagged hosts"""
self.set_up(stack=True, n_dps=self.NUM_DPS, n_untagged=self.NUM_HOSTS,
- switch_to_switch_links=2, hw_dpid=self.hw_dpid, use_external=True)
+ switch_to_switch_links=2, use_external=True)
self.verify_stack_up()
int_hosts, ext_hosts, dp_hosts = self.map_int_ext_hosts()
- _, root_ext_hosts = dp_hosts[self.DP_NAME]
+ _, root_ext_hosts = dp_hosts[self.topo.switches_by_id[0]]
for int_host in int_hosts:
# All internal hosts can reach other internal hosts.
@@ -412,14 +561,14 @@ class FaucetStackRingOfDPTest(FaucetMultiDPTest):
self.retry_net_ping()
self.verify_traveling_dhcp_mac()
# Move through each DP breaking either side of the ring
- for dpid_i in range(self.NUM_DPS):
- dpid = self.dpids[dpid_i]
- dp_name = self.dp_name(dpid_i)
- for link in self.non_host_links(dpid):
- port = link.port
- self.one_stack_port_down(dpid, dp_name, port)
+ for link, ports in self.link_port_maps.items():
+ dp_i, _ = link
+ dpid = self.topo.dpids_by_id[dp_i]
+ name = self.topo.switches_by_id[dp_i]
+ for port in ports:
+ self.one_stack_port_down(dpid, name, port)
self.retry_net_ping()
- self.one_stack_port_up(dpid, dp_name, port)
+ self.one_stack_port_up(dpid, name, port)
class FaucetSingleStack4RingOfDPTest(FaucetStackRingOfDPTest):
@@ -461,6 +610,7 @@ class FaucetSingleStack3RingOfDPReversePortOrderTest(FaucetMultiDPTest):
class FaucetSingleStack4RingOfDPReversePortOrderTest(FaucetSingleStack3RingOfDPReversePortOrderTest):
+ """Test different port orders maintain consistent stack behaviour with size 4 ring topology"""
NUM_DPS = 4
@@ -472,7 +622,8 @@ class FaucetSingleStackAclControlTest(FaucetMultiDPTest):
NUM_HOSTS = 3
def acls(self):
- map1, map2, map3 = [self.port_maps[dpid] for dpid in self.dpids]
+ """Configuration ACLs"""
+ # 3 hosts on each DP (3 DPS)
return {
1: [
{'rule': {
@@ -480,7 +631,7 @@ class FaucetSingleStackAclControlTest(FaucetMultiDPTest):
'nw_dst': '10.1.0.2',
'actions': {
'output': {
- 'port': map1['port_2']
+ 'port': self.host_port_maps[1][0][0] # Host 1
}
},
}},
@@ -490,8 +641,8 @@ class FaucetSingleStackAclControlTest(FaucetMultiDPTest):
'actions': {
'output': {
'ports': [
- map1['port_2'],
- map1['port_4']]
+ self.host_port_maps[1][0][0], # Host 1
+ self.link_port_maps[(0, 1)][0]] # link (0, 1)
}
},
}},
@@ -499,7 +650,7 @@ class FaucetSingleStackAclControlTest(FaucetMultiDPTest):
'dl_type': IPV4_ETH,
'actions': {
'output': {
- 'port': map1['port_4']
+ 'port': self.link_port_maps[(0, 1)][0] # link (0, 1)
}
},
}},
@@ -514,7 +665,7 @@ class FaucetSingleStackAclControlTest(FaucetMultiDPTest):
'dl_type': IPV4_ETH,
'actions': {
'output': {
- 'port': map2['port_5']
+ 'port': self.link_port_maps[(1, 2)][0] # link (1, 2)
}
},
}},
@@ -530,7 +681,7 @@ class FaucetSingleStackAclControlTest(FaucetMultiDPTest):
'nw_dst': '10.1.0.7',
'actions': {
'output': {
- 'port': map3['port_1']
+ 'port': self.host_port_maps[6][2][0] # host 6
}
},
}},
@@ -539,7 +690,7 @@ class FaucetSingleStackAclControlTest(FaucetMultiDPTest):
'dl_dst': 'ff:ff:ff:ff:ff:ff',
'actions': {
'output': {
- 'ports': [map3['port_1']]
+ 'ports': [self.host_port_maps[6][2][0]] # host 6
}
},
}},
@@ -558,24 +709,16 @@ class FaucetSingleStackAclControlTest(FaucetMultiDPTest):
}
# DP-to-acl_in port mapping.
- def acl_in_dp(self):
- map1, map2, map3 = [self.port_maps[dpid] for dpid in self.dpids]
+ def link_acls(self):
+ """Host/link map to acls_in"""
return {
- 0: {
- # Port 1, acl_in = 1
- map1['port_1']: 1,
- },
- 1: {
- # Port 4, acl_in = 2
- map2['port_4']: 2,
- },
- 2: {
- # Port 4, acl_in = 3
- map3['port_4']: 3,
- },
+ 0: [1], # Host 0 dp 0 'acls_in': [1]
+ (1, 0): [2],
+ (2, 1): [3]
}
def setUp(self): # pylint: disable=invalid-name
+ """Setup network & create configuration file"""
super(FaucetSingleStackAclControlTest, self).set_up(
stack=True,
n_dps=self.NUM_DPS,
@@ -584,22 +727,30 @@ class FaucetSingleStackAclControlTest(FaucetMultiDPTest):
def test_unicast(self):
"""Hosts in stack topology can appropriately reach each other over unicast."""
- hosts = self.hosts_name_ordered()
+ host0 = self.net.get(self.topo.hosts_by_id[0])
+ host1 = self.net.get(self.topo.hosts_by_id[1])
+ host3 = self.net.get(self.topo.hosts_by_id[3])
+ host6 = self.net.get(self.topo.hosts_by_id[6])
+ host7 = self.net.get(self.topo.hosts_by_id[7])
self.verify_stack_up()
- self.verify_tp_dst_notblocked(5000, hosts[0], hosts[1], table_id=None)
- self.verify_tp_dst_blocked(5000, hosts[0], hosts[3], table_id=None)
- self.verify_tp_dst_notblocked(5000, hosts[0], hosts[6], table_id=None)
- self.verify_tp_dst_blocked(5000, hosts[0], hosts[7], table_id=None)
+ self.verify_tp_dst_notblocked(5000, host0, host1, table_id=None)
+ self.verify_tp_dst_blocked(5000, host0, host3, table_id=None)
+ self.verify_tp_dst_notblocked(5000, host0, host6, table_id=None)
+ self.verify_tp_dst_blocked(5000, host0, host7, table_id=None)
self.verify_no_cable_errors()
def test_broadcast(self):
"""Hosts in stack topology can appropriately reach each other over broadcast."""
- hosts = self.hosts_name_ordered()
+ host0 = self.net.get(self.topo.hosts_by_id[0])
+ host1 = self.net.get(self.topo.hosts_by_id[1])
+ host3 = self.net.get(self.topo.hosts_by_id[3])
+ host6 = self.net.get(self.topo.hosts_by_id[6])
+ host7 = self.net.get(self.topo.hosts_by_id[7])
self.verify_stack_up()
- self.verify_bcast_dst_notblocked(5000, hosts[0], hosts[1])
- self.verify_bcast_dst_blocked(5000, hosts[0], hosts[3])
- self.verify_bcast_dst_notblocked(5000, hosts[0], hosts[6])
- self.verify_bcast_dst_blocked(5000, hosts[0], hosts[7])
+ self.verify_bcast_dst_notblocked(5000, host0, host1)
+ self.verify_bcast_dst_blocked(5000, host0, host3)
+ self.verify_bcast_dst_notblocked(5000, host0, host6)
+ self.verify_bcast_dst_blocked(5000, host0, host7)
self.verify_no_cable_errors()
@@ -610,7 +761,7 @@ class FaucetSingleStackOrderedAclControlTest(FaucetMultiDPTest):
NUM_HOSTS = 3
def acls(self):
- map1, map2, map3 = [self.port_maps[dpid] for dpid in self.dpids]
+ """Configuration ACLs"""
return {
1: [
{'rule': {
@@ -618,7 +769,7 @@ class FaucetSingleStackOrderedAclControlTest(FaucetMultiDPTest):
'nw_dst': '10.1.0.2',
'actions': {
'output': [
- {'port': map1['port_2']}
+ {'port': self.host_port_maps[1][0][0]} # Host 0
]
},
}},
@@ -628,8 +779,8 @@ class FaucetSingleStackOrderedAclControlTest(FaucetMultiDPTest):
'actions': {
'output': [
{'ports': [
- map1['port_2'],
- map1['port_4']]}
+ self.host_port_maps[1][0][0], # Host 0
+ self.link_port_maps[(0, 1)][0]]} # Link (0, 1)
]
},
}},
@@ -637,7 +788,7 @@ class FaucetSingleStackOrderedAclControlTest(FaucetMultiDPTest):
'dl_type': IPV4_ETH,
'actions': {
'output': [
- {'port': map1['port_4']}
+ {'port': self.link_port_maps[(0, 1)][0]} # Link (0, 1)
]
},
}},
@@ -652,7 +803,7 @@ class FaucetSingleStackOrderedAclControlTest(FaucetMultiDPTest):
'dl_type': IPV4_ETH,
'actions': {
'output': [
- {'port': map2['port_5']}
+ {'port': self.link_port_maps[(1, 2)][0]} # Link (0, 2)
]
},
}},
@@ -668,7 +819,7 @@ class FaucetSingleStackOrderedAclControlTest(FaucetMultiDPTest):
'nw_dst': '10.1.0.7',
'actions': {
'output': {
- 'port': map3['port_1']
+ 'port': self.host_port_maps[6][2][0] # Host 6
}
},
}},
@@ -677,7 +828,7 @@ class FaucetSingleStackOrderedAclControlTest(FaucetMultiDPTest):
'dl_dst': 'ff:ff:ff:ff:ff:ff',
'actions': {
'output': [
- {'ports': [map3['port_1']]}
+ {'ports': [self.host_port_maps[6][2][0]]} # Host 6
]
},
}},
@@ -695,25 +846,16 @@ class FaucetSingleStackOrderedAclControlTest(FaucetMultiDPTest):
],
}
- # DP-to-acl_in port mapping.
- def acl_in_dp(self):
- map1, map2, map3 = [self.port_maps[dpid] for dpid in self.dpids]
+ def link_acls(self):
+ """Host/link map to acls in"""
return {
- 0: {
- # Port 1, acl_in = 1
- map1['port_1']: 1,
- },
- 1: {
- # Port 4, acl_in = 2
- map2['port_4']: 2,
- },
- 2: {
- # Port 4, acl_in = 3
- map3['port_4']: 3,
- },
+ 0: [1], # Host 0 dp 0 'acls_in': [1]
+ (1, 0): [2],
+ (2, 1): [3]
}
def setUp(self): # pylint: disable=invalid-name
+ """Setup network & create configuration file"""
super(FaucetSingleStackOrderedAclControlTest, self).set_up(
stack=True,
n_dps=self.NUM_DPS,
@@ -722,22 +864,30 @@ class FaucetSingleStackOrderedAclControlTest(FaucetMultiDPTest):
def test_unicast(self):
"""Hosts in stack topology can appropriately reach each other over unicast."""
- hosts = self.hosts_name_ordered()
+ host0 = self.net.get(self.topo.hosts_by_id[0])
+ host1 = self.net.get(self.topo.hosts_by_id[1])
+ host3 = self.net.get(self.topo.hosts_by_id[3])
+ host6 = self.net.get(self.topo.hosts_by_id[6])
+ host7 = self.net.get(self.topo.hosts_by_id[7])
self.verify_stack_up()
- self.verify_tp_dst_notblocked(5000, hosts[0], hosts[1], table_id=None)
- self.verify_tp_dst_blocked(5000, hosts[0], hosts[3], table_id=None)
- self.verify_tp_dst_notblocked(5000, hosts[0], hosts[6], table_id=None)
- self.verify_tp_dst_blocked(5000, hosts[0], hosts[7], table_id=None)
+ self.verify_tp_dst_notblocked(5000, host0, host1, table_id=None)
+ self.verify_tp_dst_blocked(5000, host0, host3, table_id=None)
+ self.verify_tp_dst_notblocked(5000, host0, host6, table_id=None)
+ self.verify_tp_dst_blocked(5000, host0, host7, table_id=None)
self.verify_no_cable_errors()
def test_broadcast(self):
"""Hosts in stack topology can appropriately reach each other over broadcast."""
- hosts = self.hosts_name_ordered()
+ host0 = self.net.get(self.topo.hosts_by_id[0])
+ host1 = self.net.get(self.topo.hosts_by_id[1])
+ host3 = self.net.get(self.topo.hosts_by_id[3])
+ host6 = self.net.get(self.topo.hosts_by_id[6])
+ host7 = self.net.get(self.topo.hosts_by_id[7])
self.verify_stack_up()
- self.verify_bcast_dst_notblocked(5000, hosts[0], hosts[1])
- self.verify_bcast_dst_blocked(5000, hosts[0], hosts[3])
- self.verify_bcast_dst_notblocked(5000, hosts[0], hosts[6])
- self.verify_bcast_dst_blocked(5000, hosts[0], hosts[7])
+ self.verify_bcast_dst_notblocked(5000, host0, host1)
+ self.verify_bcast_dst_blocked(5000, host0, host3)
+ self.verify_bcast_dst_notblocked(5000, host0, host6)
+ self.verify_bcast_dst_blocked(5000, host0, host7)
self.verify_no_cable_errors()
@@ -750,6 +900,7 @@ class FaucetStringOfDPACLOverrideTest(FaucetMultiDPTest):
# ACL rules which will get overridden.
def acls(self):
+ """Return config ACLs"""
return {
1: [
{'rule': {
@@ -780,6 +931,7 @@ class FaucetStringOfDPACLOverrideTest(FaucetMultiDPTest):
# file, then reloaded into FAUCET.
@staticmethod
def acls_override():
+ """Return override ACLs option"""
return {
1: [
{'rule': {
@@ -807,22 +959,22 @@ class FaucetStringOfDPACLOverrideTest(FaucetMultiDPTest):
}
# DP-to-acl_in port mapping.
- def acl_in_dp(self):
- port_1 = self.port_map['port_1']
+ def link_acls(self):
+ """Host/link port map to acls in"""
return {
- 0: {
- # First port, acl_in = 1
- port_1: 1,
- },
+ 0: [1] # Host 0 'acls_in': [1]
}
+ def include_optional(self):
+ return [self.acls_config, self.missing_config]
+
def setUp(self): # pylint: disable=invalid-name
+ """Setup network & create configuration file"""
self.acls_config = os.path.join(self.tmpdir, 'acls.yaml')
- missing_config = os.path.join(self.tmpdir, 'missing_config.yaml')
+ self.missing_config = os.path.join(self.tmpdir, 'missing_config.yaml')
super(FaucetStringOfDPACLOverrideTest, self).set_up(
n_dps=self.NUM_DPS,
- n_untagged=self.NUM_HOSTS,
- include_optional=[self.acls_config, missing_config])
+ n_untagged=self.NUM_HOSTS)
def test_port5001_blocked(self):
"""Test that TCP port 5001 is blocked."""
@@ -830,7 +982,7 @@ class FaucetStringOfDPACLOverrideTest(FaucetMultiDPTest):
first_host, second_host = self.hosts_name_ordered()[0:2]
self.verify_tp_dst_notblocked(5001, first_host, second_host)
with open(self.acls_config, 'w') as config_file:
- config_file.write(self.get_config(acls=self.acls_override()))
+ config_file.write(self.topo.get_config(n_vlans=1, acl_options=self.acls_override()))
self.verify_faucet_reconf(cold_start=False, change_expected=True)
self.verify_tp_dst_blocked(5001, first_host, second_host)
self.verify_no_cable_errors()
@@ -841,7 +993,7 @@ class FaucetStringOfDPACLOverrideTest(FaucetMultiDPTest):
first_host, second_host = self.hosts_name_ordered()[0:2]
self.verify_tp_dst_blocked(5002, first_host, second_host)
with open(self.acls_config, 'w') as config_file:
- config_file.write(self.get_config(acls=self.acls_override()))
+ config_file.write(self.topo.get_config(n_vlans=1, acl_options=self.acls_override()))
self.verify_faucet_reconf(cold_start=False, change_expected=True)
self.verify_tp_dst_notblocked(5002, first_host, second_host)
self.verify_no_cable_errors()
@@ -857,6 +1009,7 @@ class FaucetTunnelSameDpTest(FaucetMultiDPTest):
def acls(self):
"""Return ACL config"""
+ # Tunnel from host 0 (switch 0) to host 1 (switch 0)
return {
1: [
{'rule': {
@@ -868,30 +1021,28 @@ class FaucetTunnelSameDpTest(FaucetMultiDPTest):
'tunnel': {
'type': 'vlan',
'tunnel_id': 200,
- 'dp': 'faucet-1',
- 'port': 'b%(port_2)d'}
+ 'dp': self.topo.switches_by_id[0], # Switch 0
+ 'port': self.host_port_maps[1][0][0]} # Switch 0 host 1
}
}
}}
]
}
- def acl_in_dp(self):
+ def link_acls(self):
"""DP to acl port mapping"""
- port_1 = self.port_map['port_1']
return {
- 0: {
- # First port 1, acl_in = 1
- port_1: 1,
- }
+ 0: [1] # Host 0 'acls_in': [1]
}
def test_tunnel_established(self):
"""Test a tunnel path can be created."""
self.set_up(stack=True, n_dps=self.NUM_DPS, n_untagged=self.NUM_HOSTS,
- switch_to_switch_links=self.SWITCH_TO_SWITCH_LINKS, hw_dpid=self.hw_dpid)
+ switch_to_switch_links=self.SWITCH_TO_SWITCH_LINKS)
self.verify_stack_up()
- src_host, dst_host, other_host = self.hosts_name_ordered()[:3]
+ src_host = self.net.get(self.topo.hosts_by_id[0])
+ dst_host = self.net.get(self.topo.hosts_by_id[1])
+ other_host = self.net.get(self.topo.hosts_by_id[2])
self.verify_tunnel_established(src_host, dst_host, other_host)
@@ -905,8 +1056,7 @@ class FaucetSingleTunnelTest(FaucetMultiDPTest):
def acls(self):
"""Return config ACL options"""
- dpid2 = self.dpids[1]
- port2_1 = self.port_maps[dpid2]['port_1']
+ # Tunnel from host 0 (switch 0) to host 2 (switch 1)
return {
1: [
{'rule': {
@@ -918,22 +1068,18 @@ class FaucetSingleTunnelTest(FaucetMultiDPTest):
'tunnel': {
'type': 'vlan',
'tunnel_id': 200,
- 'dp': 'faucet-2',
- 'port': port2_1}
+ 'dp': self.topo.switches_by_id[1],
+ 'port': self.host_port_maps[2][1][0]}
}
}
}}
]
}
- def acl_in_dp(self):
+ def link_acls(self):
"""DP-to-acl port mapping"""
- port_1 = self.port_map['port_1']
return {
- 0: {
- # First port 1, acl_in = 1
- port_1: 1,
- }
+ 0: [1] # Host 0 'acls_in': [1]
}
def setUp(self): # pylint: disable=invalid-name
@@ -942,22 +1088,25 @@ class FaucetSingleTunnelTest(FaucetMultiDPTest):
stack=True,
n_dps=self.NUM_DPS,
n_untagged=self.NUM_HOSTS,
- switch_to_switch_links=self.SWITCH_TO_SWITCH_LINKS,
- hw_dpid=self.hw_dpid)
+ switch_to_switch_links=self.SWITCH_TO_SWITCH_LINKS)
def test_tunnel_established(self):
"""Test a tunnel path can be created."""
self.verify_stack_up()
- src_host, other_host, dst_host = self.hosts_name_ordered()[:3]
+ src_host = self.net.get(self.topo.hosts_by_id[0])
+ dst_host = self.net.get(self.topo.hosts_by_id[2])
+ other_host = self.net.get(self.topo.hosts_by_id[1])
self.verify_tunnel_established(src_host, dst_host, other_host)
def test_tunnel_path_rerouted(self):
"""Test a tunnel path is rerouted when a link is down."""
self.verify_stack_up()
- src_host, other_host, dst_host = self.hosts_name_ordered()[:3]
+ src_host = self.net.get(self.topo.hosts_by_id[0])
+ dst_host = self.net.get(self.topo.hosts_by_id[2])
+ other_host = self.net.get(self.topo.hosts_by_id[1])
self.verify_tunnel_established(src_host, dst_host, other_host, packets=10)
- first_stack_port = self.non_host_links(self.dpid)[0].port
- self.one_stack_port_down(self.dpid, self.DP_NAME, first_stack_port)
+ first_stack_port = self.link_port_maps[(0, 1)][0]
+ self.one_stack_port_down(self.dpids[0], self.topo.switches_by_id[0], first_stack_port)
src_host, other_host, dst_host = self.hosts_name_ordered()[:3]
self.verify_tunnel_established(src_host, dst_host, other_host, packets=10)
@@ -975,7 +1124,6 @@ class FaucetTunnelLoopTest(FaucetSingleTunnelTest):
n_dps=self.NUM_DPS,
n_untagged=self.NUM_HOSTS,
switch_to_switch_links=self.SWITCH_TO_SWITCH_LINKS,
- hw_dpid=self.hw_dpid,
stack_ring=True)
@@ -988,9 +1136,8 @@ class FaucetTunnelAllowTest(FaucetTopoTestBase):
SOFTWARE_ONLY = True
def acls(self):
+ # Tunnel from host 0 (switch 0) to host 2 (switch 1)
"""Return config ACL options"""
- dpid2 = self.dpids[1]
- port2_1 = self.port_maps[dpid2]['port_1']
return {
1: [
{'rule': {
@@ -1002,8 +1149,8 @@ class FaucetTunnelAllowTest(FaucetTopoTestBase):
'tunnel': {
'type': 'vlan',
'tunnel_id': 300,
- 'dp': 'faucet-2',
- 'port': port2_1}
+ 'dp': self.topo.switches_by_id[1],
+ 'port': self.host_port_maps[2][1][0]}
}
}
}},
@@ -1015,27 +1162,35 @@ class FaucetTunnelAllowTest(FaucetTopoTestBase):
]
}
- def acl_in_dp(self):
- """DP-to-acl port mapping"""
- port_1 = self.port_map['port_1']
- return {
- 0: {
- # First port 1, acl_in = 1
- port_1: 1,
- }
- }
+
def setUp(self): # pylint: disable=invalid-name
"""Start the network"""
- super(FaucetTunnelAllowTest, self).setUp()
- stack_roots = {0: 1}
- dp_links = FaucetTopoGenerator.dp_links_networkx_graph(networkx.path_graph(self.NUM_DPS))
- # LACP host doubly connected to sw0 & sw1
+ super().setUp()
+ network_graph = networkx.path_graph(self.NUM_DPS)
+ dp_options = {}
+ for dp_i in network_graph.nodes():
+ dp_options.setdefault(dp_i, {
+ 'group_table': self.GROUP_TABLE,
+ 'ofchannel_log': self.debug_log_path + str(dp_i) if self.debug_log_path else None,
+ 'hardware': self.hardware if dp_i == 0 and self.hw_dpid else 'Open vSwitch'
+ })
+ if dp_i == 0:
+ dp_options[0]['stack'] = {'priority': 1}
+ switch_links = list(network_graph.edges())
+ link_vlans = {edge: None for edge in switch_links}
host_links = {0: [0], 1: [0], 2: [1], 3: [1]}
host_vlans = {0: 0, 1: 0, 2: 1, 3: 0}
+ host_options = {0: {'acls_in': [1]}}
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)
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
+ n_vlans=self.NUM_VLANS,
+ dp_options=dp_options,
+ host_options=host_options,
+ )
self.start_net()
def test_tunnel_continue_through_pipeline_interaction(self):
@@ -1044,7 +1199,9 @@ class FaucetTunnelAllowTest(FaucetTopoTestBase):
# and also have the packets arrive at h_{2,200} (the other end of the tunnel)
self.verify_stack_up()
# Ensure connection to the host on the other end of the tunnel can exist
- src_host, other_host, dst_host = self.hosts_name_ordered()[:3]
+ src_host = self.net.get(self.topo.hosts_by_id[0]) # h_{0,100}
+ other_host = self.net.get(self.topo.hosts_by_id[1]) # h_{1,100}
+ dst_host = self.net.get(self.topo.hosts_by_id[2]) # h_{2,200}
self.verify_tunnel_established(src_host, dst_host, other_host)
# Ensure a connection to a host not in the tunnel can exist
# this implies that the packet is also sent through the pipeline
@@ -1062,6 +1219,7 @@ class FaucetTunnelSameDpOrderedTest(FaucetMultiDPTest):
def acls(self):
"""Return ACL config"""
+ # Tunnel from host 0 (switch 0) to host 1 (switch 0)
return {
1: [
{'rule': {
@@ -1073,30 +1231,28 @@ class FaucetTunnelSameDpOrderedTest(FaucetMultiDPTest):
{'tunnel': {
'type': 'vlan',
'tunnel_id': 200,
- 'dp': 'faucet-1',
- 'port': 'b%(port_2)d'}}
+ 'dp': self.topo.switches_by_id[0],
+ 'port': self.host_port_maps[1][0][0]}}
]
}
}}
]
}
- def acl_in_dp(self):
+ def link_acls(self):
"""DP to acl port mapping"""
- port_1 = self.port_map['port_1']
return {
- 0: {
- # First port 1, acl_in = 1
- port_1: 1,
- }
+ 0: [1] # Host 0 'acls_in': [1]
}
def test_tunnel_established(self):
"""Test a tunnel path can be created."""
self.set_up(stack=True, n_dps=self.NUM_DPS, n_untagged=self.NUM_HOSTS,
- switch_to_switch_links=self.SWITCH_TO_SWITCH_LINKS, hw_dpid=self.hw_dpid)
+ switch_to_switch_links=self.SWITCH_TO_SWITCH_LINKS)
self.verify_stack_up()
- src_host, dst_host, other_host = self.hosts_name_ordered()[:3]
+ src_host = self.net.get(self.topo.hosts_by_id[0])
+ dst_host = self.net.get(self.topo.hosts_by_id[1])
+ other_host = self.net.get(self.topo.hosts_by_id[2])
self.verify_tunnel_established(src_host, dst_host, other_host)
@@ -1110,8 +1266,6 @@ class FaucetSingleTunnelOrderedTest(FaucetMultiDPTest):
def acls(self):
"""Return config ACL options"""
- dpid2 = self.dpids[1]
- port2_1 = self.port_maps[dpid2]['port_1']
return {
1: [
{'rule': {
@@ -1123,22 +1277,18 @@ class FaucetSingleTunnelOrderedTest(FaucetMultiDPTest):
{'tunnel': {
'type': 'vlan',
'tunnel_id': 200,
- 'dp': 'faucet-2',
- 'port': port2_1}}
+ 'dp': self.topo.switches_by_id[1],
+ 'port': self.host_port_maps[2][1][0]}}
]
}
}}
]
}
- def acl_in_dp(self):
- """DP-to-acl port mapping"""
- port_1 = self.port_map['port_1']
+ def link_acls(self):
+ """DP link to list of acls to apply"""
return {
- 0: {
- # First port 1, acl_in = 1
- port_1: 1,
- }
+ 0: [1] # Host 0 'acls_in': [1]
}
def setUp(self): # pylint: disable=invalid-name
@@ -1147,23 +1297,25 @@ class FaucetSingleTunnelOrderedTest(FaucetMultiDPTest):
stack=True,
n_dps=self.NUM_DPS,
n_untagged=self.NUM_HOSTS,
- switch_to_switch_links=self.SWITCH_TO_SWITCH_LINKS,
- hw_dpid=self.hw_dpid)
+ switch_to_switch_links=self.SWITCH_TO_SWITCH_LINKS)
def test_tunnel_established(self):
"""Test a tunnel path can be created."""
self.verify_stack_up()
- src_host, other_host, dst_host = self.hosts_name_ordered()[:3]
+ src_host = self.net.get(self.topo.hosts_by_id[0])
+ dst_host = self.net.get(self.topo.hosts_by_id[2])
+ other_host = self.net.get(self.topo.hosts_by_id[1])
self.verify_tunnel_established(src_host, dst_host, other_host)
def test_tunnel_path_rerouted(self):
"""Test a tunnel path is rerouted when a link is down."""
self.verify_stack_up()
- src_host, other_host, dst_host = self.hosts_name_ordered()[:3]
+ src_host = self.net.get(self.topo.hosts_by_id[0])
+ dst_host = self.net.get(self.topo.hosts_by_id[2])
+ other_host = self.net.get(self.topo.hosts_by_id[1])
self.verify_tunnel_established(src_host, dst_host, other_host, packets=10)
- first_stack_port = self.non_host_links(self.dpid)[0].port
- self.one_stack_port_down(self.dpid, self.DP_NAME, first_stack_port)
- src_host, other_host, dst_host = self.hosts_name_ordered()[:3]
+ first_stack_port = self.link_port_maps[(0, 1)][0]
+ self.one_stack_port_down(self.dpids[0], self.topo.switches_by_id[0], first_stack_port)
self.verify_tunnel_established(src_host, dst_host, other_host, packets=10)
@@ -1180,7 +1332,6 @@ class FaucetTunnelLoopOrderedTest(FaucetSingleTunnelOrderedTest):
n_dps=self.NUM_DPS,
n_untagged=self.NUM_HOSTS,
switch_to_switch_links=self.SWITCH_TO_SWITCH_LINKS,
- hw_dpid=self.hw_dpid,
stack_ring=True)
@@ -1194,8 +1345,6 @@ class FaucetTunnelAllowOrderedTest(FaucetTopoTestBase):
def acls(self):
"""Return config ACL options"""
- dpid2 = self.dpids[1]
- port2_1 = self.port_maps[dpid2]['port_1']
return {
1: [
{'rule': {
@@ -1207,8 +1356,8 @@ class FaucetTunnelAllowOrderedTest(FaucetTopoTestBase):
{'tunnel': {
'type': 'vlan',
'tunnel_id': 300,
- 'dp': 'faucet-2',
- 'port': port2_1}}
+ 'dp': self.topo.switches_by_id[1],
+ 'port': self.host_port_maps[2][1][0]}}
]
}
}},
@@ -1218,29 +1367,37 @@ class FaucetTunnelAllowOrderedTest(FaucetTopoTestBase):
}
}},
]
- }
- def acl_in_dp(self):
- """DP-to-acl port mapping"""
- port_1 = self.port_map['port_1']
- return {
- 0: {
- # First port 1, acl_in = 1
- port_1: 1,
- }
+
}
def setUp(self): # pylint: disable=invalid-name
"""Start the network"""
- super(FaucetTunnelAllowOrderedTest, self).setUp()
- stack_roots = {0: 1}
- dp_links = FaucetTopoGenerator.dp_links_networkx_graph(networkx.path_graph(self.NUM_DPS))
- # LACP host doubly connected to sw0 & sw1
+ super().setUp()
+ network_graph = networkx.path_graph(self.NUM_DPS)
+ dp_options = {}
+ for dp_i in network_graph.nodes():
+ dp_options.setdefault(dp_i, {
+ 'group_table': self.GROUP_TABLE,
+ 'ofchannel_log': self.debug_log_path + str(dp_i) if self.debug_log_path else None,
+ 'hardware': self.hardware if dp_i == 0 and self.hw_dpid else 'Open vSwitch'
+ })
+ if dp_i == 0:
+ dp_options[0]['stack'] = {'priority': 1}
+ switch_links = list(network_graph.edges())
+ link_vlans = {edge: None for edge in switch_links}
host_links = {0: [0], 1: [0], 2: [1], 3: [1]}
host_vlans = {0: 0, 1: 0, 2: 1, 3: 0}
+ host_options = {0: {'acls_in': [1]}}
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)
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
+ n_vlans=self.NUM_VLANS,
+ dp_options=dp_options,
+ host_options=host_options,
+ )
self.start_net()
def test_tunnel_continue_through_pipeline_interaction(self):
@@ -1249,7 +1406,9 @@ class FaucetTunnelAllowOrderedTest(FaucetTopoTestBase):
# and also have the packets arrive at h_{2,200} (the other end of the tunnel)
self.verify_stack_up()
# Ensure connection to the host on the other end of the tunnel can exist
- src_host, other_host, dst_host = self.hosts_name_ordered()[:3]
+ src_host = self.net.get(self.topo.hosts_by_id[0]) # h_{0,100}
+ other_host = self.net.get(self.topo.hosts_by_id[1]) # h_{1,100}
+ dst_host = self.net.get(self.topo.hosts_by_id[2]) # h_{2,200}
self.verify_tunnel_established(src_host, dst_host, other_host)
# Ensure a connection to a host not in the tunnel can exist
# this implies that the packet is also sent through the pipeline
@@ -1265,11 +1424,9 @@ class FaucetSingleUntaggedIPV4RoutingWithStackingTest(FaucetTopoTestBase):
ETH_TYPE = IPV4_ETH
NUM_DPS = 4
NUM_HOSTS = 8
+ NUM_VLANS = 3
SOFTWARE_ONLY = True
- def setUp(self):
- """Disabling allows for each test case to start the test"""
- pass
def set_up(self, n_dps, host_links=None, host_vlans=None):
"""
@@ -1278,31 +1435,51 @@ class FaucetSingleUntaggedIPV4RoutingWithStackingTest(FaucetTopoTestBase):
host_links: How to connect each host to the DPs
host_vlans: The VLAN each host is on
"""
- super(FaucetSingleUntaggedIPV4RoutingWithStackingTest, self).setUp()
- n_vlans = 3
+ network_graph = networkx.path_graph(n_dps)
+ dp_options = {}
+ for dp_i in network_graph.nodes():
+ dp_options.setdefault(dp_i, {
+ 'group_table': self.GROUP_TABLE,
+ 'ofchannel_log': self.debug_log_path + str(dp_i) if self.debug_log_path else None,
+ 'hardware': self.hardware if dp_i == 0 and self.hw_dpid else 'Open vSwitch'
+ })
+ for key, value in self.dp_options().items():
+ dp_options[dp_i][key] = value
+ if dp_i == 0:
+ dp_options[0]['stack'] = {'priority': 1}
+ switch_links = list(network_graph.edges())
+ link_vlans = {edge: None for edge in switch_links}
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)
+ if host_links is None or host_vlans is None:
+ host_links = {}
+ host_vlans = {}
+ host_n = 0
+ for dp_i in range(n_dps):
+ for vlan in range(routed_vlans):
+ host_links[host_n] = [dp_i]
+ host_vlans[host_n] = vlan
+ host_n += 1
vlan_options = {}
- for v in range(routed_vlans):
- vlan_options[v] = {
- 'faucet_mac': self.faucet_mac(v),
- 'faucet_vips': [self.faucet_vip(v)],
+ for v_i in range(routed_vlans):
+ vlan_options[v_i] = {
+ 'faucet_mac': self.faucet_mac(v_i),
+ 'faucet_vips': [self.faucet_vip(v_i)],
'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)]}
+ routers = {0: list(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)
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
+ n_vlans=self.NUM_VLANS,
+ dp_options=dp_options,
+ vlan_options=vlan_options,
+ routers=routers
+ )
self.start_net()
- @staticmethod
- def get_dp_options():
+ def dp_options(self):
"""Return DP config options"""
return {
'arp_neighbor_timeout': 2,
@@ -1357,7 +1534,7 @@ class FaucetSingleUntaggedIPV6RoutingWithStackingTest(FaucetSingleUntaggedIPV4Ro
NETPREFIX = 64
ETH_TYPE = IPV6_ETH
- def get_dp_options(self):
+ def dp_options(self):
"""Return DP config options"""
return {
'nd_neighbor_timeout': 2,
@@ -1395,33 +1572,47 @@ class FaucetSingleUntaggedVlanStackFloodTest(FaucetTopoTestBase):
def setUp(self):
"""Disabling allows for each test case to start the test"""
- pass
def set_up(self):
"""Start the network"""
- super(FaucetSingleUntaggedVlanStackFloodTest, self).setUp()
- stack_roots = {0: 1}
- dp_links = FaucetTopoGenerator.dp_links_networkx_graph(networkx.path_graph(self.NUM_DPS))
+ super().setUp()
+ network_graph = networkx.path_graph(self.NUM_DPS)
+ dp_options = {}
+ for dp_i in network_graph.nodes():
+ dp_options.setdefault(dp_i, {
+ 'group_table': self.GROUP_TABLE,
+ 'ofchannel_log': self.debug_log_path + str(dp_i) if self.debug_log_path else None,
+ 'hardware': self.hardware if dp_i == 0 and self.hw_dpid else 'Open vSwitch'
+ })
+ for key, value in self.dp_options().items():
+ dp_options[dp_i][key] = value
+ if dp_i == 0:
+ dp_options[0]['stack'] = {'priority': 1}
+ switch_links = list(network_graph.edges())
+ link_vlans = {edge: None for edge in switch_links}
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)],
+ for v_i in range(self.NUM_VLANS):
+ vlan_options[v_i] = {
+ 'faucet_mac': self.faucet_mac(v_i),
+ 'faucet_vips': [self.faucet_vip(v_i)],
'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)]}
+ routers = {0: list(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)
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
+ n_vlans=self.NUM_VLANS,
+ dp_options=dp_options,
+ vlan_options=vlan_options,
+ routers=routers
+ )
self.start_net()
- @staticmethod
- def get_dp_options():
+ def dp_options(self):
"""Return DP config options"""
return {
'arp_neighbor_timeout': 2,
@@ -1453,15 +1644,29 @@ class FaucetUntaggedStackTransitTest(FaucetTopoTestBase):
def setUp(self):
"""Set up network with transit switch with no hosts"""
- super(FaucetUntaggedStackTransitTest, self).setUp()
- stack_roots = {0: 1}
- dp_links = FaucetTopoGenerator.dp_links_networkx_graph(networkx.path_graph(self.NUM_DPS))
+ super().setUp()
+ network_graph = networkx.path_graph(self.NUM_DPS)
+ dp_options = {}
+ for dp_i in network_graph.nodes():
+ dp_options.setdefault(dp_i, {
+ 'group_table': self.GROUP_TABLE,
+ 'ofchannel_log': self.debug_log_path + str(dp_i) if self.debug_log_path else None,
+ 'hardware': self.hardware if dp_i == 0 and self.hw_dpid else 'Open vSwitch'
+ })
+ if dp_i == 0:
+ dp_options[0]['stack'] = {'priority': 1}
+ switch_links = list(network_graph.edges())
+ link_vlans = {edge: None for edge in switch_links}
host_links = {0: [0], 1: [2]}
host_vlans = {0: 0, 1: 0}
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)
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
+ n_vlans=self.NUM_VLANS,
+ dp_options=dp_options,
+ )
self.start_net()
def test_hosts_connect_over_stack_transit(self):
@@ -1480,15 +1685,29 @@ class FaucetUntaggedStackTransitVLANTest(FaucetTopoTestBase):
def setUp(self):
"""Set up network with transit switch on different VLAN"""
- super(FaucetUntaggedStackTransitVLANTest, self).setUp()
- stack_roots = {0: 1}
- dp_links = FaucetTopoGenerator.dp_links_networkx_graph(networkx.path_graph(self.NUM_DPS))
+ super().setUp()
+ network_graph = networkx.path_graph(self.NUM_DPS)
+ dp_options = {}
+ for dp_i in network_graph.nodes():
+ dp_options.setdefault(dp_i, {
+ 'group_table': self.GROUP_TABLE,
+ 'ofchannel_log': self.debug_log_path + str(dp_i) if self.debug_log_path else None,
+ 'hardware': self.hardware if dp_i == 0 and self.hw_dpid else 'Open vSwitch'
+ })
+ if dp_i == 0:
+ dp_options[0]['stack'] = {'priority': 1}
+ switch_links = list(network_graph.edges())
+ link_vlans = {edge: None for edge in switch_links}
host_links = {0: [0], 1: [1], 2: [2]}
host_vlans = {0: 0, 1: 1, 2: 0}
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)
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
+ n_vlans=self.NUM_VLANS,
+ dp_options=dp_options,
+ )
self.start_net()
def test_hosts_connect_over_stack_transit(self):
@@ -1507,8 +1726,7 @@ class FaucetSingleLAGTest(FaucetTopoTestBase):
LACP_HOST = 2
- @staticmethod
- def get_dp_options():
+ def dp_options(self):
"""Return DP config options"""
return {
'arp_neighbor_timeout': 2,
@@ -1519,7 +1737,6 @@ class FaucetSingleLAGTest(FaucetTopoTestBase):
def setUp(self):
"""Disabling allows for each test case to start the test"""
- pass
def set_up(self, lacp_host_links, host_vlans=None):
"""
@@ -1528,27 +1745,44 @@ class FaucetSingleLAGTest(FaucetTopoTestBase):
host_vlans: Default generate with one host on each VLAN, on each DP
plus one LAG host the same VLAN as hosts
"""
- super(FaucetSingleLAGTest, self).setUp()
- stack_roots = {0: 1}
- dp_links = FaucetTopoGenerator.dp_links_networkx_graph(networkx.path_graph(self.NUM_DPS))
+ super().setUp()
+ network_graph = networkx.path_graph(self.NUM_DPS)
+ dp_options = {}
+ for dp_i in network_graph.nodes():
+ dp_options.setdefault(dp_i, {
+ 'group_table': self.GROUP_TABLE,
+ 'ofchannel_log': self.debug_log_path + str(dp_i) if self.debug_log_path else None,
+ 'hardware': self.hardware if dp_i == 0 and self.hw_dpid else 'Open vSwitch'
+ })
+ for key, value in self.dp_options().items():
+ dp_options[dp_i][key] = value
+ if dp_i == 0:
+ dp_options[0]['stack'] = {'priority': 1}
+ switch_links = list(network_graph.edges())
+ link_vlans = {edge: None for edge in switch_links}
host_links = {0: [0], 1: [0], self.LACP_HOST: lacp_host_links, 3: [1], 4: [1]}
if host_vlans is None:
host_vlans = {0: 0, 1: 1, 2: 1, 3: 0, 4: 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)],
+ for v_i in range(self.NUM_VLANS):
+ vlan_options[v_i] = {
+ 'faucet_mac': self.faucet_mac(v_i),
+ 'faucet_vips': [self.faucet_vip(v_i)],
'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)]}
+ routers = {0: list(range(self.NUM_VLANS))}
host_options = {self.LACP_HOST: {'lacp': 1}}
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, host_options=host_options, routers=routers)
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
+ n_vlans=self.NUM_VLANS,
+ dp_options=dp_options,
+ vlan_options=vlan_options,
+ routers=routers,
+ host_options=host_options
+ )
self.start_net()
def test_lacp_lag(self):
@@ -1569,24 +1803,24 @@ class FaucetSingleLAGTest(FaucetTopoTestBase):
"""Down a port on port_dpid_index, cold-start on cold_start_dp, UP previous port"""
# Bring a LACP port DOWN
chosen_dpid = self.dpids[port_dp_index]
- port_no = self.host_information[self.LACP_HOST]['ports'][chosen_dpid][0]
+ port_no = self.host_port_maps[self.LACP_HOST][port_dp_index][0]
self.set_port_down(port_no, chosen_dpid)
self.verify_num_lag_up_ports(1, chosen_dpid)
# Cold start switch, cold-start twice to get back to initial condition
- cold_start_dpid = self.dpids[cold_start_dp_index]
conf = self._get_faucet_conf()
- interfaces_conf = conf['dps'][self.dp_name(cold_start_dp_index)]['interfaces']
+ interfaces_conf = conf['dps'][self.topo.switches_by_id[cold_start_dp_index]]['interfaces']
for port, port_conf in interfaces_conf.items():
if 'lacp' not in port_conf and 'stack' not in port_conf:
# Change host VLAN to enable cold-starting on faucet-2
curr_vlan = port_conf['native_vlan']
port_conf['native_vlan'] = (
- self.vlan_name(1) if curr_vlan == self.vlan_name(0) else self.vlan_name(0))
+ self.topo.vlan_name(1)
+ if curr_vlan == self.topo.vlan_name(0) else self.topo.vlan_name(0))
# VLAN changed so just delete the host information instead of recomputing
# routes etc..
for _id in self.host_information:
- if cold_start_dpid in self.host_information[_id]['ports']:
- ports = self.host_information[_id]['ports'][cold_start_dpid]
+ if cold_start_dp_index in self.host_port_maps[_id]:
+ ports = self.host_port_maps[_id][cold_start_dp_index]
if port in ports:
del self.host_information[_id]
break
@@ -1598,7 +1832,8 @@ class FaucetSingleLAGTest(FaucetTopoTestBase):
self.set_port_up(port_no, chosen_dpid)
self.verify_num_lag_up_ports(2, chosen_dpid)
# Take down all of the other ports
- for dpid, ports in self.host_information[self.LACP_HOST]['ports'].items():
+ for dp_i, ports in self.host_port_maps[self.LACP_HOST].items():
+ dpid = self.topo.dpids_by_id[dp_i]
if dpid != chosen_dpid:
for port in ports:
if port != port_no:
@@ -1629,10 +1864,11 @@ class FaucetSingleLAGTest(FaucetTopoTestBase):
self.verify_stack_up()
self.verify_lag_host_connectivity()
chosen_dpid = self.dpids[0]
- port_no = self.host_information[self.LACP_HOST]['ports'][chosen_dpid][0]
+ port_no = self.host_port_maps[self.LACP_HOST][0][0]
self.set_port_down(port_no, chosen_dpid)
self.set_port_up(port_no, chosen_dpid)
- for dpid, ports in self.host_information[self.LACP_HOST]['ports'].items():
+ for dp_i, ports in self.host_port_maps[self.LACP_HOST].items():
+ dpid = self.topo.dpids_by_id[dp_i]
for port in ports:
if dpid != chosen_dpid and port != port_no:
self.set_port_down(port, dpid)
@@ -1664,8 +1900,8 @@ class FaucetSingleMCLAGComplexTest(FaucetTopoTestBase):
LACP_HOST = 3
- @staticmethod
- def get_dp_options():
+ def dp_options(self):
+ """Return config DP options"""
return {
'arp_neighbor_timeout': 2,
'max_resolve_backoff_time': 2,
@@ -1674,21 +1910,37 @@ class FaucetSingleMCLAGComplexTest(FaucetTopoTestBase):
}
def setUp(self):
- pass
+ """Ignore to allow for setting up network in each test"""
def set_up(self):
- super(FaucetSingleMCLAGComplexTest, self).setUp()
- stack_roots = {0: 1}
- dp_links = FaucetTopoGenerator.dp_links_networkx_graph(networkx.path_graph(self.NUM_DPS))
- # LACP host doubly connected to sw0 & sw1
+ """Set up network"""
+ super().setUp()
+ network_graph = networkx.path_graph(self.NUM_DPS)
+ dp_options = {}
+ for dp_i in network_graph.nodes():
+ dp_options.setdefault(dp_i, {
+ 'group_table': self.GROUP_TABLE,
+ 'ofchannel_log': self.debug_log_path + str(dp_i) if self.debug_log_path else None,
+ 'hardware': self.hardware if dp_i == 0 and self.hw_dpid else 'Open vSwitch'
+ })
+ for key, value in self.dp_options().items():
+ dp_options[dp_i][key] = value
+ if dp_i == 0:
+ dp_options[0]['stack'] = {'priority': 1}
+ switch_links = list(network_graph.edges())
+ link_vlans = {edge: None for edge in switch_links}
host_links = {0: [0], 1: [1], 2: [2], 3: [0, 0, 2, 2]}
host_vlans = {host_id: 0 for host_id in range(self.NUM_HOSTS)}
- dp_options = {dp: self.get_dp_options() for dp in range(self.NUM_DPS)}
host_options = {self.LACP_HOST: {'lacp': 1}}
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,
- dp_options=dp_options, host_options=host_options)
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
+ n_vlans=self.NUM_VLANS,
+ dp_options=dp_options,
+ host_options=host_options
+ )
self.start_net()
def test_lag_connectivity(self):
@@ -1707,7 +1959,9 @@ class FaucetSingleMCLAGComplexTest(FaucetTopoTestBase):
self.verify_stack_up()
self.require_linux_bond_up(self.LACP_HOST)
lacp_host = self.host_information[self.LACP_HOST]['host']
- lacp_switches = {self.net.switches[i] for i in self.host_links[self.LACP_HOST]}
+ lacp_switches = {
+ self.net.get(self.topo.switches_by_id[i])
+ for i in self.host_port_maps[self.LACP_HOST]}
lacp_intfs = sorted({
pair[0].name for switch in lacp_switches for pair in lacp_host.connectionsTo(switch)})
dst_host_id = 1
@@ -1764,8 +2018,8 @@ class FaucetSingleMCLAGComplexTest(FaucetTopoTestBase):
self.require_linux_bond_up(self.LACP_HOST)
self.verify_lag_host_connectivity()
root_dpid = self.dpids[0]
- lacp_ports = self.host_information[self.LACP_HOST]['ports']
- for port in lacp_ports[root_dpid]:
+ lacp_ports = self.host_port_maps[self.LACP_HOST][0]
+ for port in lacp_ports:
self.set_port_down(port, root_dpid)
self.verify_num_lag_up_ports(0, root_dpid)
self.verify_lag_host_connectivity()
@@ -1784,7 +2038,9 @@ class FaucetSingleMCLAGComplexTest(FaucetTopoTestBase):
self.verify_stack_up()
self.require_linux_bond_up(self.LACP_HOST)
lacp_host = self.host_information[self.LACP_HOST]['host']
- lacp_switches = {self.net.switches[i] for i in self.host_links[self.LACP_HOST]}
+ lacp_switches = {
+ self.net.get(self.topo.switches_by_id[i])
+ for i in self.host_port_maps[self.LACP_HOST]}
lacp_intfs = {
pair[0].name for switch in lacp_switches for pair in lacp_host.connectionsTo(switch)}
dst_host = self.host_information[1]['host']
@@ -1836,7 +2092,7 @@ class FaucetStackTopoChangeTest(FaucetMultiDPTest):
stack_event_found = True
graph = event.get('STACK_TOPO_CHANGE').get('graph')
self.assertTrue(graph)
- nodeCount = len(graph.get('nodes'))
- self.assertEqual(nodeCount, 3,
- 'Number of nodes in graph object is %s (!=3)' % nodeCount)
+ node_count = len(graph.get('nodes'))
+ self.assertEqual(node_count, 3,
+ 'Number of nodes in graph object is %s (!=3)' % node_count)
self.assertTrue(stack_event_found)
diff --git a/tests/tolerance/mininet_main.py b/tests/tolerance/mininet_main.py
index 7a8631b7..e40edf97 100755
--- a/tests/tolerance/mininet_main.py
+++ b/tests/tolerance/mininet_main.py
@@ -14,18 +14,15 @@ import networkx
from networkx.generators.atlas import graph_atlas_g
from clib.clib_mininet_test_main import test_main
-from clib.mininet_test_topo_generator import FaucetTopoGenerator
import mininet_tests
-def test_generator(num_dps, num_vlans, n_dp_links, stack_roots, func_graph):
+def test_generator(func_graph, stack_roots):
"""Return the function that will start the fault-tolerance testing for a graph"""
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.set_up(func_graph, stack_roots)
self.network_function()
return test
@@ -43,9 +40,7 @@ if __name__ == '__main__':
for i, test_class in enumerate(mininet_tests.TEST_CLASS_LIST):
for test_graph in GRAPHS[test_class.NUM_DPS]:
test_name = 'test_%s' % test_graph.name
- test_func = test_generator(
- test_class.NUM_DPS, test_class.NUM_VLANS,
- test_class.N_DP_LINKS, test_class.STACK_ROOTS, test_graph)
+ test_func = test_generator(test_graph, test_class.STACK_ROOTS)
setattr(test_class, test_name, test_func)
test_main([mininet_tests.__name__])
diff --git a/tests/tolerance/mininet_tests.py b/tests/tolerance/mininet_tests.py
index a17f1e14..5260f73d 100644
--- a/tests/tolerance/mininet_tests.py
+++ b/tests/tolerance/mininet_tests.py
@@ -4,7 +4,6 @@ import random
import unittest
import networkx
-from clib.mininet_test_topo_generator import FaucetTopoGenerator
from clib.mininet_test_watcher import TopologyWatcher
from clib.mininet_test_base_topo import FaucetTopoTestBase
@@ -51,48 +50,80 @@ class FaucetFaultToleranceBaseTest(FaucetTopoTestBase):
seed = 1
rng = None
+ # Number of VLANs to create, if >= 2 then routing will be applied
+ NUM_VLANS = None
+ # Number of DPs in the network
+ NUM_DPS = None
+ # Number of links between switches
+ N_DP_LINKS = None
+
+ host_links = None
+ switch_links = None
+ routers = None
+ stack_roots = None
+
def setUp(self):
pass
- def set_up(self, n_dps, n_vlans, dp_links, stack_roots,
- host_links=None, host_vlans=None, host_options=None):
+ def set_up(self, network_graph, stack_roots, host_links=None, host_vlans=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)
+ network_graph (networkx.MultiGraph): Network topology for the test
+ stack_roots (dict): The priority values for the stack roots
+ host_links (dict): Links for each host to switches
+ host_vlans (dict): VLAN for each host
"""
- super(FaucetFaultToleranceBaseTest, self).setUp()
+ super().setUp()
+ switch_links = list(network_graph.edges()) * self.N_DP_LINKS
+ link_vlans = {edge: None for edge in switch_links}
if not host_links or not host_vlans:
- host_links, host_vlans = FaucetTopoGenerator.untagged_vlan_hosts(n_dps, n_vlans)
+ # Setup normal host links & vlans
+ host_links = {}
+ host_vlans = {}
+ host_n = 0
+ for dp_i in network_graph.nodes():
+ for v in range(self.NUM_VLANS):
+ host_links[host_n] = [dp_i]
+ host_vlans[host_n] = v
+ host_n += 1
+ dp_options = {}
+ for i in network_graph.nodes():
+ dp_options.setdefault(i, {
+ 'group_table': self.GROUP_TABLE,
+ 'ofchannel_log': self.debug_log_path + str(i) if self.debug_log_path else None,
+ 'hardware': 'Open vSwitch'
+ })
+ if i in stack_roots:
+ dp_options[i]['stack'] = {'priority': stack_roots[i]}
vlan_options = {}
- if n_vlans >= 2:
- for i in range(n_vlans):
+ routers = {}
+ if self.NUM_VLANS >= 2:
+ # Setup options for routing
+ routers = {0: list(range(self.NUM_VLANS))}
+ for i in range(self.NUM_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] = {
- 'arp_neighbor_timeout': 2,
- 'max_resolve_backoff_time': 2,
- 'proactive_learn_v4': True
- }
- routers = {}
- if n_vlans >= 2:
- routers = {0: list(range(n_vlans))}
+ for i in network_graph.nodes():
+ dp_options[i]['arp_neighbor_timeout'] = 2
+ dp_options[i]['max_resolve_backoff_time'] = 2
+ dp_options[i]['proactive_learn_v4'] = True
+ self.host_links = host_links
+ self.switch_links = switch_links
+ self.routers = routers
+ self.stack_roots = stack_roots
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)
+ host_links=host_links,
+ host_vlans=host_vlans,
+ switch_links=switch_links,
+ link_vlans=link_vlans,
+ n_vlans=self.NUM_VLANS,
+ dp_options=dp_options,
+ vlan_options=vlan_options,
+ routers=routers
+ )
self.start_net()
def host_connectivity(self, host, dst):
@@ -146,12 +177,12 @@ class FaucetFaultToleranceBaseTest(FaucetTopoTestBase):
"""
index = args[0]
dpid = self.dpids[index]
- switch_name = self.topo.dpid_names[dpid]
+ switch_name = self.topo.switches_by_id[index]
switch = next((switch for switch in self.net.switches if switch.name == switch_name), None)
if switch is None:
return
self.dump_switch_flows(switch)
- name = '%s:%s DOWN' % (self.dp_name(index), self.dpids[index])
+ name = '%s:%s DOWN' % (self.topo.switches_by_id[index], self.dpids[index])
self.topo_watcher.add_switch_fault(index, name)
switch.stop()
switch.cmd(self.VSCTL, 'del-controller', switch.name, '|| true')
@@ -165,8 +196,7 @@ class FaucetFaultToleranceBaseTest(FaucetTopoTestBase):
dpid_list = self.topo_watcher.get_eligable_switch_events()
if len(self.stack_roots.keys()) <= 1:
# Prevent only root from being destroyed
- sorted_roots = {
- k: v for k, v in sorted(self.stack_roots.items(), key=lambda item: item[1])}
+ sorted_roots = dict(sorted(self.stack_roots.items(), key=lambda item: item[1]))
for root_index in sorted_roots.keys():
root_dpid = self.dpids[root_index]
if root_dpid in dpid_list:
@@ -190,18 +220,18 @@ class FaucetFaultToleranceBaseTest(FaucetTopoTestBase):
dst_i = args[1]
src_dpid = self.dpids[src_i]
dst_dpid = self.dpids[dst_i]
- s1 = self.dp_name(src_i)
- s2 = self.dp_name(dst_i)
- for link in self.topo.dpid_peer_links(src_dpid):
- port, peer_dpid, peer_port = link.port, link.peer_dpid, link.peer_port
- status = self.stack_port_status(src_dpid, self.dp_name(src_i), port)
- if peer_dpid == dst_dpid and status == 3:
+ s1_name = self.topo.switches_by_id[src_i]
+ s2_name = self.topo.switches_by_id[dst_i]
+ for port, link in self.topo.ports[s1_name].items():
+ status = self.stack_port_status(src_dpid, s1_name, port)
+ if link[0] == s2_name and status == 3:
+ peer_port = link[1]
self.set_port_down(port, src_dpid)
self.set_port_down(peer_port, dst_dpid)
- self.wait_for_stack_port_status(src_dpid, self.dp_name(src_i), port, 4)
- self.wait_for_stack_port_status(dst_dpid, self.dp_name(dst_i), peer_port, 4)
+ self.wait_for_stack_port_status(src_dpid, s1_name, port, 4)
+ self.wait_for_stack_port_status(dst_dpid, s2_name, peer_port, 4)
name = 'Link %s[%s]:%s-%s[%s]:%s DOWN' % (
- s1, src_dpid, port, s2, dst_dpid, peer_port)
+ s1_name, src_dpid, port, s2_name, dst_dpid, peer_port)
self.topo_watcher.add_link_fault(src_i, dst_i, name)
return
@@ -253,8 +283,8 @@ class FaucetFaultToleranceBaseTest(FaucetTopoTestBase):
self.rng = random.Random(self.seed)
self.topo_watcher = TopologyWatcher(
- self.dpids, self.dp_links, self.host_links,
- self.n_vlans, self.host_information, self.routers)
+ self.dpids, self.switch_links, self.host_links,
+ self.NUM_VLANS, self.host_information, self.routers)
# Calculate stats (before any tear downs)
self.calculate_connectivity()
@@ -322,41 +352,36 @@ class FaucetSingleFaultTolerance4DPTest(FaucetFaultToleranceBaseTest):
"""Test fat-tree-pod-2 randomly tearing down only switches"""
fault_events = [(self.random_switch_fault, (None,)) for _ in range(self.NUM_DPS)]
stack_roots = {2*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.set_up(networkx.cycle_graph(self.NUM_DPS), stack_roots)
self.network_function(fault_events=fault_events)
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))]
+ network_graph = networkx.cycle_graph(self.NUM_DPS)
+ fault_events = [(self.random_dp_link_fault, (None,)) for _ in range(len(network_graph.edges()))]
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.set_up(network_graph, stack_roots)
self.network_function(fault_events=fault_events)
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.set_up(networkx.cycle_graph(self.NUM_DPS), stack_roots)
self.network_function(fault_events=fault_events)
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)
+ self.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.set_up(networkx.cycle_graph(self.NUM_DPS), stack_roots)
self.network_function(fault_events=fault_events, num_faults=num_faults)
+ self.N_DP_LINKS = 1
@unittest.skip('Too expensive for Travis to run')
diff --git a/tests/unit/clib/test_topo.py b/tests/unit/clib/test_topo.py
index b761c5bc..b0962ed0 100755
--- a/tests/unit/clib/test_topo.py
+++ b/tests/unit/clib/test_topo.py
@@ -1,154 +1,189 @@
+#!/usr/bin/env python3
+
"""Unit tests for Mininet Topologies in mininet_test_topo"""
from unittest import TestCase, main
-import networkx
-
-from clib.mininet_test_topo_generator import FaucetTopoGenerator
-from clib.mininet_test_util import flat_test_name
+from clib.config_generator import FaucetFakeOFTopoGenerator
-class FaucetStringOfDPSwitchTopoTest(TestCase):
- """Tests for FaucetStringOfDPSwitchTopoTest"""
+class FaucetTopoTest(TestCase):
+ """Tests for Faucet test suite mininet Topo class generator"""
serial = 0
- maxDiff = None
- dpids = ['1', '2', '3']
- vlan_vids = [100]
+
+ START_PORT = 5
+ PORT_ORDER = [0, 1, 2, 3]
+
+ class FakeExtendedHost:
+ """Fake class for a mininet extended host"""
def get_serialno(self, *_args, **_kwargs):
""""Return mock serial number"""
self.serial += 1
return self.serial
- def string_of_dp_args(self, **kwargs):
- """Return default topo constructor params"""
- defaults = dict(
- ovs_type='user',
- ports_sock=None,
- dpids=self.dpids,
- test_name=flat_test_name(self.id()),
+ def test_port_order(self):
+ """Test port order extension & port order option"""
+ port_order = [3, 2, 1, 0]
+ extended = FaucetFakeOFTopoGenerator.extend_port_order(port_order, max_length=8)
+ self.assertEqual(extended, [3, 2, 1, 0, 7, 6, 5, 4])
+ port_order = [1, 2, 3, 4, 0]
+ extended = FaucetFakeOFTopoGenerator.extend_port_order(port_order, max_length=10)
+ self.assertEqual(extended, [1, 2, 3, 4, 0, 6, 7, 8, 9, 5])
+ host_links = {0: [0], 1: [1]}
+ host_vlans = {0: 0, 1: 0}
+ switch_links = [(0, 1)]
+ link_vlans = {(0, 1): [0]}
+ port_order = [3, 2, 1, 0]
+ expected_ports = [self.START_PORT + port for port in port_order]
+ topo = FaucetFakeOFTopoGenerator(
+ '', '', '',
+ host_links, host_vlans, switch_links, link_vlans,
+ start_port=self.START_PORT, port_order=port_order,
get_serialno=self.get_serialno)
- defaults.update(kwargs)
- return defaults
-
- 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}
-
+ s1_name = topo.switches_by_id[0]
+ s1_ports = list(topo.ports[s1_name].keys())
+ self.assertEqual(s1_ports, expected_ports[:2])
+ s2_name = topo.switches_by_id[1]
+ s2_ports = list(topo.ports[s2_name].keys())
+ self.assertEqual(s2_ports, expected_ports[:2])
+
+ def test_start_port(self):
+ """Test the topology start port parameter option"""
+ start_port = 55
+ host_links = {0: [0], 1: [1]}
+ host_vlans = {0: 0, 1: 0}
+ switch_links = [(0, 1)]
+ link_vlans = {(0, 1): [0]}
+ port_order = [3, 2, 1, 0]
+ expected_ports = [start_port + port for port in port_order]
+ topo = FaucetFakeOFTopoGenerator(
+ '', '', '',
+ host_links, host_vlans, switch_links, link_vlans,
+ start_port=start_port, port_order=port_order,
+ get_serialno=self.get_serialno)
+ s1_name, s2_name = topo.switches_by_id.values()
+ h1_name, h2_name = topo.hosts_by_id.values()
+ self.assertEqual(topo.ports[s1_name][expected_ports[0]][0], s2_name)
+ self.assertEqual(topo.ports[s2_name][expected_ports[0]][0], s1_name)
+ self.assertEqual(topo.ports[s1_name][expected_ports[1]][0], h1_name)
+ self.assertEqual(topo.ports[s2_name][expected_ports[1]][0], h2_name)
+
+ def test_hw_build(self):
+ """Test the topology is built with hardware requirements"""
+ host_links = {0: [0], 1: [1]}
+ host_vlans = {0: 0, 1: 0}
+ switch_links = [(0, 1)]
+ link_vlans = {(0, 1): [0]}
+ hw_dpid = 0x123
+ hw_ports = {1: 'p1', 2: 'p2', 3: 'p3', 4: 'p4', 5: 'p5', 6: 'p6'}
+ topo = FaucetFakeOFTopoGenerator(
+ '', '', '',
+ host_links, host_vlans, switch_links, link_vlans,
+ hw_dpid=hw_dpid, hw_ports=hw_ports,
+ start_port=self.START_PORT, port_order=self.PORT_ORDER,
+ get_serialno=self.get_serialno)
+ self.assertEqual(topo.dpids_by_id[0], hw_dpid)
+ self.assertEqual(list(topo.ports[topo.switches_by_id[0]].keys()), [1, 2])
+
+ def test_no_links(self):
+ """Test single switch topology"""
+ host_links = {0: [0]}
+ host_vlans = {0: 0}
+ switch_links = {}
+ link_vlans = {}
+ topo = FaucetFakeOFTopoGenerator(
+ '', '', '',
+ host_links, host_vlans, switch_links, link_vlans,
+ start_port=self.START_PORT, port_order=self.PORT_ORDER,
+ get_serialno=self.get_serialno)
+ self.assertEqual(len(topo.hosts()), 1)
+ self.assertEqual(len(topo.switches()), 1)
+ self.assertEqual(len(topo.links()), 1)
+ host_name = topo.hosts_by_id[0]
+ switch_name = topo.switches_by_id[0]
+ self.assertEqual((switch_name, host_name), topo.links()[0])
+
+ def test_build(self):
+ """Test the topology is built correctly"""
+ host_links = {0: [0], 1: [1]}
+ host_vlans = {0: 0, 1: [0, 1]}
+ switch_links = [(0, 1), (0, 1), (0, 1)]
+ link_vlans = {(0, 1): [0, 1]}
+ topo = FaucetFakeOFTopoGenerator(
+ '', '', '',
+ host_links, host_vlans, switch_links, link_vlans,
+ start_port=self.START_PORT, port_order=self.PORT_ORDER,
+ get_serialno=self.get_serialno)
+ self.assertEqual(len(topo.dpids_by_id), 2)
+ self.assertEqual(len(topo.hosts_by_id), 2)
+ self.assertEqual(len(topo.switches_by_id), 2)
+ _, host_port_maps, link_port_maps = topo.create_port_maps()
+ self.assertEqual(len(link_port_maps[(0, 1)]), 3)
+ self.assertEqual(len(host_port_maps[0]), 1)
+ self.assertEqual(len(host_port_maps[1]), 1)
+ host0, host1 = topo.hosts_by_id.values()
+ dp0, dp1 = topo.switches_by_id.values()
+ links = topo.links()
+ self.assertIn((dp0, host0), links)
+ self.assertIn((dp1, host1), links)
+ self.assertIn((dp0, dp1), links)
+ self.assertEqual(links.count((dp0, dp1)), 3)
+
+ def test_host_options(self):
+ """Test the topology correctly provides mininet host options"""
+ host_options = {
+ 0: {'inNamespace': True, 'ip': '127.0.0.1'},
+ 1: {'cls': self.FakeExtendedHost}}
+ host_links = {0: [0], 1: [0]}
+ host_vlans = {0: 0, 1: None}
+ switch_links = []
+ link_vlans = {}
+ topo = FaucetFakeOFTopoGenerator(
+ '', '', '',
+ host_links, host_vlans, switch_links, link_vlans,
+ host_options=host_options,
+ start_port=self.START_PORT, port_order=self.PORT_ORDER,
+ get_serialno=self.get_serialno)
+ for host_id, opts in host_options.items():
+ info = topo.nodeInfo(topo.hosts_by_id[host_id])
+ for key, value in opts.items():
+ self.assertIn(key, info)
+ self.assertEqual(value, info[key])
+
+ def test_link_port_map(self):
+ """Test correctly generated link port map"""
+ host_links = {0: [0], 1: [1]}
+ host_vlans = {0: 0, 1: 0}
+ switch_links = [(0, 1), (0, 1), (1, 2)]
+ link_vlans = {edge: None for edge in switch_links}
+ topo = FaucetFakeOFTopoGenerator(
+ '', '', '',
+ host_links, host_vlans, switch_links, link_vlans,
+ start_port=self.START_PORT, port_order=self.PORT_ORDER,
+ get_serialno=self.get_serialno)
+ link_port_maps = topo._create_link_port_map()
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")
-
- 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}
-
+ link_port_maps,
+ {(0, 1): [5, 6], (1, 0): [5, 6], (1, 2): [7], (2, 1): [5]})
+
+ def test_host_port_map(self):
+ """Test correctly generated host port map"""
+ host_links = {0: [0, 2], 1: [1]}
+ host_vlans = {0: 0, 1: 0}
+ switch_links = [(0, 1), (0, 1), (1, 2)]
+ link_vlans = {edge: None for edge in switch_links}
+ topo = FaucetFakeOFTopoGenerator(
+ '', '', '',
+ host_links, host_vlans, switch_links, link_vlans,
+ start_port=self.START_PORT, port_order=self.PORT_ORDER,
+ get_serialno=self.get_serialno)
+ host_port_maps = topo._create_host_port_map()
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}
+ host_port_maps,
+ {0: {0: [7], 2: [6]}, 1: {1: [8]}})
- 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")
if __name__ == "__main__":
main()