Update of latest tests.
[csit.git] / resources / libraries / python / NodePath.py
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:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
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.
13
14 """Path utilities library for nodes in the topology."""
15
16 from topology import Topology
17
18
19 class NodePath(object):
20     """Path utilities for nodes in the topology.
21
22     :Example:
23
24     node1--link1-->node2--link2-->node3--link3-->node2--link4-->node1
25     RobotFramework:
26     | Library | resources/libraries/python/NodePath.py
27
28     | Path test
29     | | [Arguments] | ${node1} | ${node2} | ${node3}
30     | | Append Node | ${nodes1}
31     | | Append Node | ${nodes2}
32     | | Append Nodes | ${nodes3} | ${nodes2}
33     | | Append Node | ${nodes1}
34     | | Compute Path | ${FALSE}
35     | | ${first_int} | ${node}= | First Interface
36     | | ${last_int} | ${node}= | Last Interface
37     | | ${first_ingress} | ${node}= | First Ingress Interface
38     | | ${last_egress} | ${node}= | Last Egress Interface
39     | | ${next} | ${node}= | Next Interface
40
41     Python:
42     >>> from NodePath import NodePath
43     >>> path = NodePath()
44     >>> path.append_node(node1)
45     >>> path.append_node(node2)
46     >>> path.append_nodes(node3, node2)
47     >>> path.append_node(node1)
48     >>> path.compute_path()
49     >>> (interface, node) = path.first_interface()
50     >>> (interface, node) = path.last_interface()
51     >>> (interface, node) = path.first_ingress_interface()
52     >>> (interface, node) = path.last_egress_interface()
53     >>> (interface, node) = path.next_interface()
54     """
55
56     def __init__(self):
57         self._nodes = []
58         self._links = []
59         self._path = []
60         self._path_iter = []
61
62     def append_node(self, node):
63         """Append node to the path.
64
65         :param node: Node to append to the path.
66         :type node: dict
67         """
68         self._nodes.append(node)
69
70     def append_nodes(self, *nodes):
71         """Append nodes to the path.
72
73         :param nodes: Nodes to append to the path.
74         :type nodes: dict
75
76         .. note:: Node order does matter.
77         """
78         for node in nodes:
79             self.append_node(node)
80
81     def clear_path(self):
82         """Clear path."""
83         self._nodes = []
84         self._links = []
85         self._path = []
86         self._path_iter = []
87
88     def compute_path(self, always_same_link=True):
89         """Compute path for added nodes.
90
91         :param always_same_link: If True use always same link between two nodes
92             in path. If False use different link (if available) between two
93             nodes if one link was used before.
94         :type always_same_link: bool
95
96         .. note:: First add at least two nodes to the topology.
97         """
98         nodes = self._nodes
99         if len(nodes) < 2:
100             raise RuntimeError('Not enough nodes to compute path')
101
102         for idx in range(0, len(nodes) - 1):
103             topo = Topology()
104             node1 = nodes[idx]
105             node2 = nodes[idx + 1]
106             links = topo.get_active_connecting_links(node1, node2)
107             if not links:
108                 raise RuntimeError('No link between {0} and {1}'.format(
109                     node1['host'], node2['host']))
110
111             link = None
112             l_set = set()
113
114             if always_same_link:
115                 l_set = set(links).intersection(self._links)
116             else:
117                 l_set = set(links).difference(self._links)
118
119             if not l_set:
120                 link = links.pop()
121             else:
122                 link = l_set.pop()
123
124             self._links.append(link)
125             interface1 = topo.get_interface_by_link_name(node1, link)
126             interface2 = topo.get_interface_by_link_name(node2, link)
127             self._path.append((interface1, node1))
128             self._path.append((interface2, node2))
129
130         self._path_iter.extend(self._path)
131         self._path_iter.reverse()
132
133     def next_interface(self):
134         """Path interface iterator.
135
136         :return: Interface and node or None if not next interface.
137         :rtype: tuple (str, dict)
138
139         .. note:: Call compute_path before.
140         """
141         if not self._path_iter:
142             return (None, None)
143         else:
144             return self._path_iter.pop()
145
146     def first_interface(self):
147         """Return first interface on the path.
148
149         :return: Interface and node.
150         :rtype: tuple (str, dict)
151
152         .. note:: Call compute_path before.
153         """
154         if not self._path:
155             raise RuntimeError('No path for topology')
156         return self._path[0]
157
158     def last_interface(self):
159         """Return last interface on the path.
160
161         :return: Interface and node.
162         :rtype: tuple (str, dict)
163
164         .. note:: Call compute_path before.
165         """
166         if not self._path:
167             raise RuntimeError('No path for topology')
168         return self._path[-1]
169
170     def first_ingress_interface(self):
171         """Return first ingress interface on the path.
172
173         :return: Interface and node.
174         :rtype: tuple (str, dict)
175
176         .. note:: Call compute_path before.
177         """
178         if not self._path:
179             raise RuntimeError('No path for topology')
180         return self._path[1]
181
182     def last_egress_interface(self):
183         """Return last egress interface on the path.
184
185         :return: Interface and node.
186         :rtype: tuple (str, dict)
187
188         .. note:: Call compute_path before.
189         """
190         if not self._path:
191             raise RuntimeError('No path for topology')
192         return self._path[-2]