def vpp_setup_bidirectional_cross_connect(node, interface1, interface2): """Create bidirectional cross-connect between 2 interfaces on vpp node. :param node: Node to add bidirectional cross-connect. :param interface1: First interface name or sw_if_index. :param interface2: Second interface name or sw_if_index. :type node: dict :type interface1: str or int :type interface2: str or int """ if isinstance(interface1, basestring): sw_iface1 = Topology().get_interface_sw_index(node, interface1) else: sw_iface1 = interface1 if isinstance(interface2, basestring): sw_iface2 = Topology().get_interface_sw_index(node, interface2) else: sw_iface2 = interface2 with VatTerminal(node) as vat: vat.vat_terminal_exec_cmd_from_template('l2_xconnect.vat', interface1=sw_iface1, interface2=sw_iface2) vat.vat_terminal_exec_cmd_from_template('l2_xconnect.vat', interface1=sw_iface2, interface2=sw_iface1)
def vpp_setup_bidirectional_l2_patch(node, interface1, interface2): """Create bidirectional l2 patch between 2 interfaces on vpp node. :param node: Node to add bidirectional l2 patch. :param interface1: First interface name or sw_if_index. :param interface2: Second interface name or sw_if_index. :type node: dict :type interface1: str or int :type interface2: str or int """ if isinstance(interface1, basestring): sw_iface1 = Topology().get_interface_sw_index(node, interface1) else: sw_iface1 = interface1 if isinstance(interface2, basestring): sw_iface2 = Topology().get_interface_sw_index(node, interface2) else: sw_iface2 = interface2 cmd = 'l2_patch_add_del' args1 = dict(rx_sw_if_index=sw_iface1, tx_sw_if_index=sw_iface2, is_add=1) args2 = dict(rx_sw_if_index=sw_iface2, tx_sw_if_index=sw_iface1, is_add=1) err_msg = 'Failed to add L2 patch between two interfaces on' \ ' host {host}'.format(host=node['host']) with PapiExecutor(node) as papi_exec: papi_exec.add(cmd, **args1).add(cmd, **args2).get_replies(err_msg).\ verify_replies(err_msg=err_msg)
def vpp_setup_bidirectional_l2_patch(node, interface1, interface2): """Create bidirectional l2 patch between 2 interfaces on vpp node. :param node: Node to add bidirectional l2 patch. :param interface1: First interface name or sw_if_index. :param interface2: Second interface name or sw_if_index. :type node: dict :type interface1: str or int :type interface2: str or int """ if isinstance(interface1, str): sw_iface1 = Topology().get_interface_sw_index(node, interface1) else: sw_iface1 = interface1 if isinstance(interface2, str): sw_iface2 = Topology().get_interface_sw_index(node, interface2) else: sw_iface2 = interface2 cmd = u"l2_patch_add_del" args1 = dict(rx_sw_if_index=sw_iface1, tx_sw_if_index=sw_iface2, is_add=True) args2 = dict(rx_sw_if_index=sw_iface2, tx_sw_if_index=sw_iface1, is_add=True) err_msg = f"Failed to add L2 patch between two interfaces " \ f"on host {node['host']}" with PapiSocketExecutor(node) as papi_exec: papi_exec.add(cmd, **args1).add(cmd, **args2).get_replies(err_msg)
def vpp_get_ipv46_interface_counter(self, node, interface, is_ipv6=True): """Return interface IPv4/IPv6 counter. :param node: Node to get interface IPv4/IPv6 counter on. :param interface: Interface name. :param is_ipv6: Specify IP version. :type node: dict :type interface: str :type is_ipv6: bool :returns: Interface IPv4/IPv6 counter. :rtype: int """ version = 'ip6' if is_ipv6 else 'ip4' topo = Topology() if_index = topo.get_interface_sw_index(node, interface) if if_index is None: logger.trace('{i} sw_index not found.'.format(i=interface)) return 0 if_counters = self._stats_table.get('interface_counters') if if_counters is None or len(if_counters) == 0: logger.trace('No interface counters.') return 0 for counter in if_counters: if counter['vnet_counter_type'] == version: data = counter['data'] return data[if_index] logger.trace('{i} {v} counter not found.'.format(i=interface, v=version)) return 0
def vpp_nodes_set_ipv4_addresses(nodes, nodes_addr): """Set IPv4 addresses on all VPP nodes in topology. :param nodes: Nodes of the test topology. :param nodes_addr: Available nodes IPv4 addresses. :type nodes: dict :type nodes_addr: dict :returns: Affected interfaces as list of (node, interface) tuples. :rtype: list """ interfaces = [] for net in nodes_addr.values(): for port in net['ports'].values(): host = port.get('node') if host is None: continue topo = Topology() node = topo.get_node_by_hostname(nodes, host) if node is None: continue if node['type'] != NodeType.DUT: continue iface_key = topo.get_interface_by_name(node, port['if']) get_node(node).set_ip(iface_key, port['addr'], net['prefix']) interfaces.append((node, port['if'])) return interfaces
def get_sw_if_index(self, interface): """Get sw_if_index of specified interface from current node. :param interface: Interface name. :type interface: str :returns: sw_if_index of the interface or None. :rtype: int """ return Topology().get_interface_sw_index(self.node_info, interface)
def compute_path(self, always_same_link=True): """Compute path for added nodes. .. note:: First add at least two nodes to the topology. :param always_same_link: If True use always same link between two nodes in path. If False use different link (if available) between two nodes if one link was used before. :type always_same_link: bool :raises RuntimeError: If not enough nodes for path. """ nodes = self._nodes if len(nodes) < 2: raise RuntimeError(u"Not enough nodes to compute path") for idx in range(0, len(nodes) - 1): topo = Topology() node1 = nodes[idx] node2 = nodes[idx + 1] n1_list = self._nodes_filter[idx] n2_list = self._nodes_filter[idx + 1] links = topo.get_active_connecting_links(node1, node2, filter_list_node1=n1_list, filter_list_node2=n2_list) if not links: raise RuntimeError( f"No link between {node1[u'host']} and {node2[u'host']}") # Not using set operations, as we need deterministic order. if always_same_link: l_set = [link for link in links if link in self._links] else: l_set = [link for link in links if link not in self._links] if not l_set: raise RuntimeError( f"No free link between {node1[u'host']} and " f"{node2[u'host']}, all links already used") if not l_set: link = links[0] else: link = l_set[0] self._links.append(link) interface1 = topo.get_interface_by_link_name(node1, link) interface2 = topo.get_interface_by_link_name(node2, link) self._path.append((interface1, node1)) self._path.append((interface2, node2)) self._path_iter.extend(self._path) self._path_iter.reverse()
def compute_path(self, always_same_link=True): """Compute path for added nodes. .. note:: First add at least two nodes to the topology. :param always_same_link: If True use always same link between two nodes in path. If False use different link (if available) between two nodes if one link was used before. :type always_same_link: bool :raises RuntimeError: If not enough nodes for path. """ nodes = self._nodes if len(nodes) < 2: raise RuntimeError('Not enough nodes to compute path') for idx in range(0, len(nodes) - 1): topo = Topology() node1 = nodes[idx] node2 = nodes[idx + 1] n1_list = self._nodes_filter[idx] n2_list = self._nodes_filter[idx + 1] links = topo.get_active_connecting_links(node1, node2, filter_list_node1=n1_list, filter_list_node2=n2_list) if not links: raise RuntimeError('No link between {0} and {1}'.format( node1['host'], node2['host'])) if always_same_link: l_set = set(links).intersection(self._links) else: l_set = set(links).difference(self._links) if not l_set: raise RuntimeError( 'No free link between {0} and {1}, all links already ' 'used'.format(node1['host'], node2['host'])) if not l_set: link = links.pop() else: link = l_set.pop() self._links.append(link) interface1 = topo.get_interface_by_link_name(node1, link) interface2 = topo.get_interface_by_link_name(node2, link) self._path.append((interface1, node1)) self._path.append((interface2, node2)) self._path_iter.extend(self._path) self._path_iter.reverse()
def get_variables(nodes, networks=IPV4_NETWORKS[:]): """Special robot framework method that returns dictionary nodes_ipv4_addr, mapping of node and interface name to IPv4 address. :param nodes: Nodes of the test topology. :param networks: List of available IPv4 networks. :type nodes: dict :type networks: list .. note:: Robot framework calls it automatically. """ topo = Topology() links = topo.get_links(nodes) if len(links) > len(networks): raise Exception('Not enough available IPv4 networks for topology.') ip4_n = IPv4NetworkGenerator(networks) nets = {} for link in links: ip4_net = ip4_n.next_network() net_hosts = ip4_net.hosts() port_idx = 0 ports = {} for node in nodes.values(): if_key = topo.get_interface_by_link_name(node, link) if_name = topo.get_interface_name(node, if_key) if if_name is not None: port = { 'addr': str(next(net_hosts)), 'node': node['host'], 'if': if_name } port_idx += 1 port_id = 'port{0}'.format(port_idx) ports.update({port_id: port}) nets.update({ link: { 'net_addr': str(ip4_net.network_address), 'prefix': ip4_net.prefixlen, 'ports': ports } }) return {'DICT__nodes_ipv4_addr': nets}
def generate_duplicate_lisp_locator_set_data(node, locator_set_number): """Generate a list of lisp locator_set we want set to VPP and then check if it is set correctly. Some locator_sets are duplicated. :param node: VPP node. :param locator_set_number: Generate n locator_set. :type node: dict :type locator_set_number: str :returns: list of lisp locator_set, list of lisp locator_set expected from VAT. :rtype: tuple """ topo = Topology() locator_set_list = [] locator_set_list_vat = [] i = 0 for num in range(0, int(locator_set_number)): locator_list = [] for interface in node['interfaces'].values(): link = interface.get('link') i += 1 if link is None: continue if_name = topo.get_interface_by_link_name(node, link) sw_if_index = topo.get_interface_sw_index(node, if_name) if if_name is not None: l_name = 'ls{0}'.format(num) locator = { 'locator-index': sw_if_index, 'priority': i, 'weight': i } locator_list.append(locator) locator_set = { 'locator-set': l_name, 'locator': locator_list } locator_set_list.append(locator_set) locator_set_vat = {"ls_name": l_name, "ls_index": num} locator_set_list_vat.append(locator_set_vat) return locator_set_list, locator_set_list_vat
def nodes_clear_ipv6_addresses(self, nodes, nodes_addr): """Remove IPv6 addresses from all VPP nodes in topology. :param nodes: Nodes of the test topology. :param nodes_addr: Available nodes IPv6 addresses. :type nodes: dict :type nodes_addr: dict """ for net in nodes_addr.values(): for port in net['ports'].values(): host = port.get('node') if host is None: continue topo = Topology() node = topo.get_node_by_hostname(nodes, host) if node is None: continue if node['type'] == NodeType.DUT: self.vpp_del_if_ipv6_addr(node, port['if'], port['addr'], net['prefix'])
def create_bridge_domain_vat_dict(node, link_names, bd_id): """Create dictionary that can be used in l2 bridge domain template. The resulting dictionary looks like this: 'interface1': interface name of first interface 'interface2': interface name of second interface 'bd_id': bridge domain index :param node: Node data dictionary. :param link_names: List of names of links the bridge domain should be connecting. :param bd_id: Bridge domain index number. :type node: dict :type link_names: list :returns: Dictionary used to generate l2 bridge domain VAT configuration from template file. :rtype: dict """ bd_dict = Topology().get_interfaces_by_link_names(node, link_names) bd_dict['bd_id'] = bd_id return bd_dict
def initialize_traffic_generator(self, tg_node, tg_if1, tg_if2, tg_if1_adj_node, tg_if1_adj_if, tg_if2_adj_node, tg_if2_adj_if, test_type, tg_if1_dst_mac=None, tg_if2_dst_mac=None): """TG initialization. :param tg_node: Traffic generator node. :param tg_if1: TG - name of first interface. :param tg_if2: TG - name of second interface. :param tg_if1_adj_node: TG if1 adjecent node. :param tg_if1_adj_if: TG if1 adjecent interface. :param tg_if2_adj_node: TG if2 adjecent node. :param tg_if2_adj_if: TG if2 adjecent interface. :param test_type: 'L2' or 'L3' - src/dst MAC address. :param tg_if1_dst_mac: Interface 1 destination MAC address. :param tg_if2_dst_mac: Interface 2 destination MAC address. :type tg_node: dict :type tg_if1: str :type tg_if2: str :type tg_if1_adj_node: dict :type tg_if1_adj_if: str :type tg_if2_adj_node: dict :type tg_if2_adj_if: str :type test_type: str :type tg_if1_dst_mac: str :type tg_if2_dst_mac: str :returns: nothing :raises: RuntimeError in case of issue during initialization. """ topo = Topology() if tg_node['type'] != NodeType.TG: raise RuntimeError('Node type is not a TG') self._node = tg_node if tg_node['subtype'] == NodeSubTypeTG.TREX: trex_path = "/opt/trex-core-2.25" ssh = SSH() ssh.connect(tg_node) (ret, stdout, stderr) = ssh.exec_command( "sudo -E sh -c '{}/resources/tools/t-rex/" "t-rex-installer.sh'".format(Constants.REMOTE_FW_DIR), timeout=1800) if int(ret) != 0: logger.error('trex installation failed: {0}'.format(stdout + stderr)) raise RuntimeError('Installation of TG failed') if1_pci = topo.get_interface_pci_addr(tg_node, tg_if1) if2_pci = topo.get_interface_pci_addr(tg_node, tg_if2) if1_mac = topo.get_interface_mac(tg_node, tg_if1) if2_mac = topo.get_interface_mac(tg_node, tg_if2) if test_type == 'L2': if1_adj_mac = if2_mac if2_adj_mac = if1_mac elif test_type == 'L3': if1_adj_mac = topo.get_interface_mac(tg_if1_adj_node, tg_if1_adj_if) if2_adj_mac = topo.get_interface_mac(tg_if2_adj_node, tg_if2_adj_if) else: raise ValueError("test_type unknown") if tg_if1_dst_mac is not None and tg_if2_dst_mac is not None: if1_adj_mac = tg_if1_dst_mac if2_adj_mac = tg_if2_dst_mac if min(if1_pci, if2_pci) != if1_pci: if1_mac, if2_mac = if2_mac, if1_mac if1_pci, if2_pci = if2_pci, if1_pci if1_adj_mac, if2_adj_mac = if2_adj_mac, if1_adj_mac self._ifaces_reordered = True if1_mac_hex = "0x" + if1_mac.replace(":", ",0x") if2_mac_hex = "0x" + if2_mac.replace(":", ",0x") if1_adj_mac_hex = "0x" + if1_adj_mac.replace(":", ",0x") if2_adj_mac_hex = "0x" + if2_adj_mac.replace(":", ",0x") (ret, stdout, stderr) = ssh.exec_command( "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n" "- port_limit : 2\n" " version : 2\n" " interfaces : [\"{}\",\"{}\"]\n" " port_info :\n" " - dest_mac : [{}]\n" " src_mac : [{}]\n" " - dest_mac : [{}]\n" " src_mac : [{}]\n" "EOF'"\ .format(if1_pci, if2_pci, if1_adj_mac_hex, if1_mac_hex, if2_adj_mac_hex, if2_mac_hex)) if int(ret) != 0: logger.error("failed to create t-rex config: {}"\ .format(stdout + stderr)) raise RuntimeError('trex config generation error') max_startup_retries = 3 while max_startup_retries > 0: # kill T-rex only if it is already running (ret, _, _) = ssh.exec_command( "sh -c 'pgrep t-rex && sudo pkill t-rex && sleep 3'") # configure T-rex (ret, stdout, stderr) = ssh.exec_command( "sh -c 'cd {0}/scripts/ && sudo ./trex-cfg'"\ .format(trex_path)) if int(ret) != 0: logger.error('trex-cfg failed: {0}'.format(stdout + stderr)) raise RuntimeError('trex-cfg failed') # start T-rex (ret, _, _) = ssh.exec_command( "sh -c 'cd {0}/scripts/ && " "sudo nohup ./t-rex-64 -i -c 7 --iom 0 > /dev/null 2>&1 &'" "> /dev/null"\ .format(trex_path)) if int(ret) != 0: raise RuntimeError('t-rex-64 startup failed') # get T-rex server info (ret, _, _) = ssh.exec_command( "sh -c 'sleep 3; " "{0}/resources/tools/t-rex/t-rex-server-info.py'"\ .format(Constants.REMOTE_FW_DIR), timeout=120) if int(ret) == 0: # If we get info T-rex is running return # try again max_startup_retries -= 1 # after max retries T-rex is still not responding to API # critical error occurred raise RuntimeError('t-rex-64 startup failed')