X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2FNodePath.py;fp=resources%2Flibraries%2Fpython%2FNodePath.py;h=d1aa1f76d4bbf21c4d7b6d1fafb4e5e102a7bd29;hp=0000000000000000000000000000000000000000;hb=b92a827b1c7f48da4214e992e5503ebe1c182416;hpb=33499c81c94c2d3baef9d3e9f061cd76ef86fa74 diff --git a/resources/libraries/python/NodePath.py b/resources/libraries/python/NodePath.py new file mode 100644 index 0000000000..d1aa1f76d4 --- /dev/null +++ b/resources/libraries/python/NodePath.py @@ -0,0 +1,192 @@ +# Copyright (c) 2016 Cisco and/or its affiliates. +# 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. + +"""Path utilities library for nodes in the topology.""" + +from topology import Topology + + +class NodePath(object): + """Path utilities for nodes in the topology. + + :Example: + + node1--link1-->node2--link2-->node3--link3-->node2--link4-->node1 + RobotFramework: + | Library | resources/libraries/python/NodePath.py + + | Path test + | | [Arguments] | ${node1} | ${node2} | ${node3} + | | Append Node | ${nodes1} + | | Append Node | ${nodes2} + | | Append Nodes | ${nodes3} | ${nodes2} + | | Append Node | ${nodes1} + | | Compute Path | ${FALSE} + | | ${first_int} | ${node}= | First Interface + | | ${last_int} | ${node}= | Last Interface + | | ${first_ingress} | ${node}= | First Ingress Interface + | | ${last_egress} | ${node}= | Last Egress Interface + | | ${next} | ${node}= | Next Interface + + Python: + >>> from NodePath import NodePath + >>> path = NodePath() + >>> path.append_node(node1) + >>> path.append_node(node2) + >>> path.append_nodes(node3, node2) + >>> path.append_node(node1) + >>> path.compute_path() + >>> (interface, node) = path.first_interface() + >>> (interface, node) = path.last_interface() + >>> (interface, node) = path.first_ingress_interface() + >>> (interface, node) = path.last_egress_interface() + >>> (interface, node) = path.next_interface() + """ + + def __init__(self): + self._nodes = [] + self._links = [] + self._path = [] + self._path_iter = [] + + def append_node(self, node): + """Append node to the path. + + :param node: Node to append to the path. + :type node: dict + """ + self._nodes.append(node) + + def append_nodes(self, *nodes): + """Append nodes to the path. + + :param nodes: Nodes to append to the path. + :type nodes: dict + + .. note:: Node order does matter. + """ + for node in nodes: + self.append_node(node) + + def clear_path(self): + """Clear path.""" + self._nodes = [] + self._links = [] + self._path = [] + self._path_iter = [] + + def compute_path(self, always_same_link=True): + """Compute path for added nodes. + + :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 + + .. note:: First add at least two nodes to the topology. + """ + 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] + links = topo.get_active_connecting_links(node1, node2) + if not links: + raise RuntimeError('No link between {0} and {1}'.format( + node1['host'], node2['host'])) + + link = None + l_set = set() + + if always_same_link: + l_set = set(links).intersection(self._links) + else: + l_set = set(links).difference(self._links) + + 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 next_interface(self): + """Path interface iterator. + + :return: Interface and node or None if not next interface. + :rtype: tuple (str, dict) + + .. note:: Call compute_path before. + """ + if not self._path_iter: + return (None, None) + else: + return self._path_iter.pop() + + def first_interface(self): + """Return first interface on the path. + + :return: Interface and node. + :rtype: tuple (str, dict) + + .. note:: Call compute_path before. + """ + if not self._path: + raise RuntimeError('No path for topology') + return self._path[0] + + def last_interface(self): + """Return last interface on the path. + + :return: Interface and node. + :rtype: tuple (str, dict) + + .. note:: Call compute_path before. + """ + if not self._path: + raise RuntimeError('No path for topology') + return self._path[-1] + + def first_ingress_interface(self): + """Return first ingress interface on the path. + + :return: Interface and node. + :rtype: tuple (str, dict) + + .. note:: Call compute_path before. + """ + if not self._path: + raise RuntimeError('No path for topology') + return self._path[1] + + def last_egress_interface(self): + """Return last egress interface on the path. + + :return: Interface and node. + :rtype: tuple (str, dict) + + .. note:: Call compute_path before. + """ + if not self._path: + raise RuntimeError('No path for topology') + return self._path[-2]