1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Defines nodes and topology structure."""
16 from robot.api import logger
17 from robot.libraries.BuiltIn import BuiltIn
18 from robot.api.deco import keyword
21 __all__ = ["DICT__nodes", 'Topology']
24 def load_topo_from_yaml():
25 """Load topology from file defined in "${TOPOLOGY_PATH}" variable
27 :return: nodes from loaded topology
29 topo_path = BuiltIn().get_variable_value("${TOPOLOGY_PATH}")
31 with open(topo_path) as work_file:
32 return load(work_file.read())['nodes']
35 class NodeType(object):
36 """Defines node types used in topology dictionaries"""
37 # Device Under Test (this node has VPP running on it)
39 # Traffic Generator (this node has traffic generator on it)
41 # Virtual Machine (this node running on DUT node)
45 class NodeSubTypeTG(object):
46 #T-Rex traffic generator
53 DICT__nodes = load_topo_from_yaml()
56 class Topology(object):
57 """Topology data manipulation and extraction methods
59 Defines methods used for manipulation and extraction of data from
64 def get_node_by_hostname(nodes, hostname):
65 """Get node from nodes of the topology by hostname.
67 :param nodes: Nodes of the test topology.
68 :param hostname: Host name.
71 :return: Node dictionary or None if not found.
73 for node in nodes.values():
74 if node['host'] == hostname:
81 """Get list of links(networks) in the topology.
83 :param nodes: Nodes of the test topology.
85 :return: Links in the topology.
90 for node in nodes.values():
91 for interface in node['interfaces'].values():
92 link = interface.get('link')
100 def _get_interface_by_key_value(node, key, value):
101 """Return node interface name according to key and value
103 :param node: :param node: the node dictionary
104 :param key: key by which to select the interface.
105 :param value: value that should be found using the key.
109 interfaces = node['interfaces']
111 for interface in interfaces.values():
112 k_val = interface.get(key)
113 if k_val is not None:
115 retval = interface['name']
119 def get_interface_by_link_name(self, node, link_name):
120 """Return interface name of link on node.
122 This method returns the interface name asociated with a given link
124 :param link_name: name of the link that a interface is connected to.
125 :param node: the node topology dictionary
126 :return: interface name of the interface connected to the given link
129 return self._get_interface_by_key_value(node, "link", link_name)
131 def get_interfaces_by_link_names(self, node, link_names):
132 """Return dictionary of dicitonaries {"interfaceN", interface name}.
134 This method returns the interface names asociated with given links
136 :param link_names: list of names of the link that a interface is
138 :param node: the node topology directory
139 :return: dictionary of interface names that are connected to the given
144 interface_key_tpl = "interface{}"
146 for link_name in link_names:
147 interface_name = self.get_interface_by_link_name(node, link_name)
148 interface_key = interface_key_tpl.format(str(interface_number))
149 retval[interface_key] = interface_name
150 interface_number += 1
153 def get_interface_by_sw_index(self, node, sw_index):
154 """Return interface name of link on node.
156 This method returns the interface name asociated with a software index
157 assigned to the interface by vpp for a given node.
158 :param sw_index: sw_index of the link that a interface is connected to.
159 :param node: the node topology dictionary
160 :return: interface name of the interface connected to the given link
163 return self._get_interface_by_key_value(node, "vpp_sw_index", sw_index)
166 def get_interface_sw_index(node, interface):
167 """Get VPP sw_if_index for the interface.
169 :param node: Node to get interface sw_if_index on.
170 :param interface: Interface identifier.
172 :type interface: str or int
173 :return: Return sw_if_index or None if not found.
176 return int(interface)
178 for port in node['interfaces'].values():
179 port_name = port.get('name')
180 if port_name == interface:
181 return port.get('vpp_sw_index')
185 def get_interface_mtu(node, interface):
186 """Get interface MTU.
188 Returns physical layer MTU (max. size of Ethernet frame).
189 :param node: Node to get interface MTU on.
190 :param interface: Interface name.
193 :return: MTU or None if not found.
196 for port in node['interfaces'].values():
197 port_name = port.get('name')
198 if port_name == interface:
199 return port.get('mtu')
204 def get_interface_mac_by_port_key(node, port_key):
205 """Get MAC address for the interface based on port key.
207 :param node: Node to get interface mac on.
208 :param port_key: Dictionary key name of interface.
211 :return: Return MAC or None if not found.
213 for port_name, port_data in node['interfaces'].iteritems():
214 if port_name == port_key:
215 return port_data['mac_address']
220 def get_interface_mac(node, interface):
221 """Get MAC address for the interface.
223 :param node: Node to get interface sw_index on.
224 :param interface: Interface name.
227 :return: Return MAC or None if not found.
229 for port in node['interfaces'].values():
230 port_name = port.get('name')
231 if port_name == interface:
232 return port.get('mac_address')
237 def get_adjacent_node_and_interface_by_key(nodes_info, node, port_key):
238 """Get node and interface adjacent to specified interface
241 :param nodes_info: Dictionary containing information on all nodes
243 :param node: Node that contains specified interface.
244 :param port_key: Interface port key.
245 :type nodes_info: dict
248 :return: Return (node, interface info) tuple or None if not found.
252 # get link name where the interface belongs to
253 for port_name, port_data in node['interfaces'].iteritems():
254 if port_name == 'mgmt':
256 if port_name == port_key:
257 link_name = port_data['link']
260 if link_name is None:
264 for node_data in nodes_info.values():
266 if node_data['host'] == node['host']:
268 for interface, interface_data \
269 in node_data['interfaces'].iteritems():
270 if 'link' not in interface_data:
272 if interface_data['link'] == link_name:
273 return node_data, node_data['interfaces'][interface]
276 def get_adjacent_node_and_interface(nodes_info, node, interface_name):
277 """Get node and interface adjacent to specified interface
280 :param nodes_info: Dictionary containing information on all nodes
282 :param node: Node that contains specified interface.
283 :param interface_name: Interface name.
284 :type nodes_info: dict
286 :type interface_name: str
287 :return: Return (node, interface info) tuple or None if not found.
291 # get link name where the interface belongs to
292 for port_name, port_data in node['interfaces'].iteritems():
293 if port_name == 'mgmt':
295 if port_data['name'] == interface_name:
296 link_name = port_data['link']
299 if link_name is None:
303 for node_data in nodes_info.values():
305 if node_data['host'] == node['host']:
307 for interface, interface_data \
308 in node_data['interfaces'].iteritems():
309 if 'link' not in interface_data:
311 if interface_data['link'] == link_name:
312 return node_data, node_data['interfaces'][interface]
315 def get_interface_pci_addr(node, interface):
316 """Get interface PCI address.
318 :param node: Node to get interface PCI address on.
319 :param interface: Interface name.
322 :return: Return PCI address or None if not found.
324 for port in node['interfaces'].values():
325 if interface == port.get('name'):
326 return port.get('pci_address')
330 def get_interface_driver(node, interface):
331 """Get interface driver.
333 :param node: Node to get interface driver on.
334 :param interface: Interface name.
337 :return: Return interface driver or None if not found.
339 for port in node['interfaces'].values():
340 if interface == port.get('name'):
341 return port.get('driver')
345 def get_node_link_mac(node, link_name):
346 """Return interface mac address by link name
348 :param node: Node to get interface sw_index on
349 :param link_name: link name
351 :type link_name: string
352 :return: mac address string
354 for port in node['interfaces'].values():
355 if port.get('link') == link_name:
356 return port.get('mac_address')
360 def _get_node_active_link_names(node):
361 """Return list of link names that are other than mgmt links
363 :param node: node topology dictionary
364 :return: list of strings that represent link names occupied by the node
366 interfaces = node['interfaces']
368 for interface in interfaces.values():
369 if 'link' in interface:
370 link_names.append(interface['link'])
371 if len(link_names) == 0:
375 @keyword('Get active links connecting "${node1}" and "${node2}"')
376 def get_active_connecting_links(self, node1, node2):
377 """Return list of link names that connect together node1 and node2
379 :param node1: node topology dictionary
380 :param node2: node topology dictionary
381 :return: list of strings that represent connecting link names
384 logger.trace("node1: {}".format(str(node1)))
385 logger.trace("node2: {}".format(str(node2)))
386 node1_links = self._get_node_active_link_names(node1)
387 node2_links = self._get_node_active_link_names(node2)
388 connecting_links = list(set(node1_links).intersection(node2_links))
390 return connecting_links
392 @keyword('Get first active connecting link between node "${node1}" and '
394 def get_first_active_connecting_link(self, node1, node2):
397 :param node1: Connected node
399 :param node2: Connected node
401 :return: name of link connecting the two nodes together
402 :raises: RuntimeError
405 connecting_links = self.get_active_connecting_links(node1, node2)
406 if len(connecting_links) == 0:
407 raise RuntimeError("No links connecting the nodes were found")
409 return connecting_links[0]
411 @keyword('Get egress interfaces on "${node1}" for link with "${node2}"')
412 def get_egress_interfaces_for_nodes(self, node1, node2):
413 """Get egress interfaces on node1 for link with node2.
415 :param node1: First node, node to get egress interface on.
416 :param node2: Second node.
419 :return: Egress interfaces.
423 links = self.get_active_connecting_links(node1, node2)
425 raise RuntimeError('No link between nodes')
426 for interface in node1['interfaces'].values():
427 link = interface.get('link')
432 name = interface.get('name')
435 interfaces.append(name)
438 @keyword('Get first egress interface on "${node1}" for link with '
440 def get_first_egress_interface_for_nodes(self, node1, node2):
441 """Get first egress interface on node1 for link with node2.
443 :param node1: First node, node to get egress interface on.
444 :param node2: Second node.
447 :return: Egress interface.
450 interfaces = self.get_egress_interfaces_for_nodes(node1, node2)
452 raise RuntimeError('No egress interface for nodes')
455 @keyword('Get link data useful in circular topology test from tg "${tgen}"'
456 ' dut1 "${dut1}" dut2 "${dut2}"')
457 def get_links_dict_from_nodes(self, tgen, dut1, dut2):
458 """Return link combinations used in tests in circular topology.
460 For the time being it returns links from the Node path:
462 :param tgen: traffic generator node data
463 :param dut1: DUT1 node data
464 :param dut2: DUT2 node data
468 :return: dictionary of possible link combinations
469 the naming convention until changed to something more general is
471 DUT1_DUT2_LINK: link name between DUT! and DUT2
472 DUT1_TG_LINK: link name between DUT1 and TG
473 DUT2_TG_LINK: link name between DUT2 and TG
474 TG_TRAFFIC_LINKS: list of link names that generated traffic is sent
476 DUT1_BD_LINKS: list of link names that will be connected by the bridge
478 DUT2_BD_LINKS: list of link names that will be connected by the bridge
481 # TODO: replace with generic function.
482 dut1_dut2_link = self.get_first_active_connecting_link(dut1, dut2)
483 dut1_tg_link = self.get_first_active_connecting_link(dut1, tgen)
484 dut2_tg_link = self.get_first_active_connecting_link(dut2, tgen)
485 tg_traffic_links = [dut1_tg_link, dut2_tg_link]
486 dut1_bd_links = [dut1_dut2_link, dut1_tg_link]
487 dut2_bd_links = [dut1_dut2_link, dut2_tg_link]
488 topology_links = {'DUT1_DUT2_LINK': dut1_dut2_link,
489 'DUT1_TG_LINK': dut1_tg_link,
490 'DUT2_TG_LINK': dut2_tg_link,
491 'TG_TRAFFIC_LINKS': tg_traffic_links,
492 'DUT1_BD_LINKS': dut1_bd_links,
493 'DUT2_BD_LINKS': dut2_bd_links}
494 return topology_links
497 def is_tg_node(node):
498 """Find out whether the node is TG
500 :param node: node to examine
501 :return: True if node is type of TG; False otherwise
503 return node['type'] == NodeType.TG
506 def get_node_hostname(node):
507 """Return host (hostname/ip address) of the node.
509 :param node: Node created from topology.
511 :return: host as 'str' type