change interface up calls to explicit keywords
[csit.git] / resources / libraries / python / IPv4Setup.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 """IPv4 setup library"""
15
16 from socket import inet_ntoa
17 from struct import pack
18 from abc import ABCMeta, abstractmethod
19 from robot.api.deco import keyword
20
21 import resources.libraries.python.ssh as ssh
22 from resources.libraries.python.Routing import Routing
23 from resources.libraries.python.InterfaceUtil import InterfaceUtil
24 from resources.libraries.python.topology import NodeType, Topology
25 from resources.libraries.python.VatExecutor import VatExecutor
26
27
28 class IPv4Node(object):
29     """Abstract class of a node in a topology."""
30     __metaclass__ = ABCMeta
31
32     def __init__(self, node_info):
33         self.node_info = node_info
34
35     @staticmethod
36     def _get_netmask(prefix_length):
37         bits = 0xffffffff ^ (1 << 32 - prefix_length) - 1
38         return inet_ntoa(pack('>I', bits))
39
40     @abstractmethod
41     def set_ip(self, interface, address, prefix_length):
42         """Configure IPv4 address on interface
43
44         :param interface: interface name
45         :param address:
46         :param prefix_length:
47         :type interface: str
48         :type address: str
49         :type prefix_length: int
50         :return: nothing
51         """
52         pass
53
54     @abstractmethod
55     def set_route(self, network, prefix_length, gateway, interface):
56         """Configure IPv4 route
57
58         :param network: network IPv4 address
59         :param prefix_length: mask length
60         :param gateway: IPv4 address of the gateway
61         :param interface: interface name
62         :type network: str
63         :type prefix_length: int
64         :type gateway: str
65         :type interface: str
66         :return: nothing
67         """
68         pass
69
70     @abstractmethod
71     def unset_route(self, network, prefix_length, gateway, interface):
72         """Remove specified IPv4 route
73
74         :param network: network IPv4 address
75         :param prefix_length: mask length
76         :param gateway: IPv4 address of the gateway
77         :param interface: interface name
78         :type network: str
79         :type prefix_length: int
80         :type gateway: str
81         :type interface: str
82         :return: nothing
83         """
84         pass
85
86     @abstractmethod
87     def flush_ip_addresses(self, interface):
88         """Flush all IPv4 addresses from specified interface
89
90         :param interface: interface name
91         :type interface: str
92         :return: nothing
93         """
94         pass
95
96     @abstractmethod
97     def ping(self, destination_address, source_interface):
98         """Send an ICMP request to destination node
99
100         :param destination_address: address to send the ICMP request
101         :param source_interface:
102         :type destination_address: str
103         :type source_interface: str
104         :return: nothing
105         """
106         pass
107
108
109 class Tg(IPv4Node):
110     """Traffic generator node"""
111     def __init__(self, node_info):
112         super(Tg, self).__init__(node_info)
113
114     def _execute(self, cmd):
115         return ssh.exec_cmd_no_error(self.node_info, cmd)
116
117     def _sudo_execute(self, cmd):
118         return ssh.exec_cmd_no_error(self.node_info, cmd, sudo=True)
119
120     def set_ip(self, interface, address, prefix_length):
121         cmd = 'ip -4 addr flush dev {}'.format(interface)
122         self._sudo_execute(cmd)
123         cmd = 'ip addr add {}/{} dev {}'.format(address, prefix_length,
124                                                 interface)
125         self._sudo_execute(cmd)
126
127     def set_route(self, network, prefix_length, gateway, interface):
128         netmask = self._get_netmask(prefix_length)
129         cmd = 'route add -net {} netmask {} gw {}'.\
130             format(network, netmask, gateway)
131         self._sudo_execute(cmd)
132
133     def unset_route(self, network, prefix_length, gateway, interface):
134         self._sudo_execute('ip route delete {}/{}'.
135                            format(network, prefix_length))
136
137     def arp_ping(self, destination_address, source_interface):
138         self._sudo_execute('arping -c 1 -I {} {}'.format(source_interface,
139                                                          destination_address))
140
141     def ping(self, destination_address, source_interface):
142         self._execute('ping -c 1 -w 5 -I {} {}'.format(source_interface,
143                                                        destination_address))
144
145     def flush_ip_addresses(self, interface):
146         self._sudo_execute('ip addr flush dev {}'.format(interface))
147
148
149 class Dut(IPv4Node):
150     """Device under test"""
151     def __init__(self, node_info):
152         super(Dut, self).__init__(node_info)
153
154     def get_sw_if_index(self, interface):
155         """Get sw_if_index of specified interface from current node
156
157         :param interface: interface name
158         :type interface: str
159         :return: sw_if_index of 'int' type
160         """
161         return Topology().get_interface_sw_index(self.node_info, interface)
162
163     def exec_vat(self, script, **args):
164         """Wrapper for VAT executor.
165
166         :param script: script to execute
167         :param args: parameters to the script
168         :type script: str
169         :type args: dict
170         :return: nothing
171         """
172         # TODO: check return value
173         VatExecutor.cmd_from_template(self.node_info, script, **args)
174
175     def set_arp(self, interface, ip_address, mac_address):
176         """Set entry in ARP cache.
177
178         :param interface: Interface name.
179         :param ip_address: IP address.
180         :param mac_address: MAC address.
181         :type interface: str
182         :type ip_address: str
183         :type mac_address: str
184         """
185         self.exec_vat('add_ip_neighbor.vat',
186                       sw_if_index=self.get_sw_if_index(interface),
187                       ip_address=ip_address, mac_address=mac_address)
188
189     def set_ip(self, interface, address, prefix_length):
190         self.exec_vat('add_ip_address.vat',
191                       sw_if_index=self.get_sw_if_index(interface),
192                       address=address, prefix_length=prefix_length)
193
194     def set_route(self, network, prefix_length, gateway, interface):
195         Routing.vpp_route_add(self.node_info,
196                       network=network, prefix_len=prefix_length,
197                       gateway=gateway, interface=interface)
198
199     def unset_route(self, network, prefix_length, gateway, interface):
200         self.exec_vat('del_route.vat', network=network,
201                       prefix_length=prefix_length, gateway=gateway,
202                       sw_if_index=self.get_sw_if_index(interface))
203
204     def arp_ping(self, destination_address, source_interface):
205         pass
206
207     def flush_ip_addresses(self, interface):
208         self.exec_vat('flush_ip_addresses.vat',
209                       sw_if_index=self.get_sw_if_index(interface))
210
211     def ping(self, destination_address, source_interface):
212         pass
213
214
215 def get_node(node_info):
216     """Creates a class instance derived from Node based on type.
217
218     :param node_info: dictionary containing information on nodes in topology
219     :return: Class instance that is derived from Node
220     """
221     if node_info['type'] == NodeType.TG:
222         return Tg(node_info)
223     elif node_info['type'] == NodeType.DUT:
224         return Dut(node_info)
225     else:
226         raise NotImplementedError('Node type "{}" unsupported!'.
227                                   format(node_info['type']))
228
229
230 class IPv4Setup(object):
231     """IPv4 setup in topology."""
232
233     @staticmethod
234     def vpp_nodes_set_ipv4_addresses(nodes, nodes_addr):
235         """Set IPv4 addresses on all VPP nodes in topology.
236
237            :param nodes: Nodes of the test topology.
238            :param nodes_addr: Available nodes IPv4 adresses.
239            :type nodes: dict
240            :type nodes_addr: dict
241            :return: affected interfaces as list of (node, interface) tuples
242            :rtype: list
243         """
244
245         interfaces = []
246         for net in nodes_addr.values():
247             for port in net['ports'].values():
248                 host = port.get('node')
249                 if host is None:
250                     continue
251                 topo = Topology()
252                 node = topo.get_node_by_hostname(nodes, host)
253                 if node is None:
254                     continue
255                 if node['type'] != NodeType.DUT:
256                     continue
257                 get_node(node).set_ip(port['if'], port['addr'], net['prefix'])
258                 interfaces.append((node, port['if']))
259
260         return interfaces
261
262     @staticmethod
263     @keyword('Get IPv4 address of node "${node}" interface "${port}" '
264              'from "${nodes_addr}"')
265     def get_ip_addr(node, interface, nodes_addr):
266         """Return IPv4 address of the node port.
267         :param node: Node in the topology.
268         :param interface: Interface name of the node.
269         :param nodes_addr: Nodes IPv4 adresses.
270         :type node: dict
271         :type interface: str
272         :type nodes_addr: dict
273         :return: IPv4 address string
274         """
275         for net in nodes_addr.values():
276             for port in net['ports'].values():
277                 host = port.get('node')
278                 dev = port.get('if')
279                 if host == node['host'] and dev == interface:
280                     ip = port.get('addr')
281                     if ip is not None:
282                         return ip
283                     else:
284                         raise Exception(
285                             'Node {n} port {p} IPv4 address is not set'.format(
286                                 n=node['host'], p=interface))
287
288         raise Exception('Node {n} port {p} IPv4 address not found.'.format(
289             n=node['host'], p=interface))
290
291     @staticmethod
292     def setup_arp_on_all_duts(nodes_info, nodes_addr):
293         """For all DUT nodes extract MAC and IP addresses of adjacent
294         interfaces from topology and use them to setup ARP entries.
295
296         :param nodes_info: Dictionary containing information on all nodes
297         in topology.
298         :param nodes_addr: Nodes IPv4 adresses.
299         :type nodes_info: dict
300         :type nodes_addr: dict
301         """
302         for node in nodes_info.values():
303             if node['type'] == NodeType.TG:
304                 continue
305             for interface, interface_data in node['interfaces'].iteritems():
306                 if interface == 'mgmt':
307                     continue
308                 interface_name = interface_data['name']
309                 adj_node, adj_int = Topology.\
310                     get_adjacent_node_and_interface(nodes_info, node,
311                                                     interface_name)
312                 ip_address = IPv4Setup.get_ip_addr(adj_node, adj_int['name'],
313                                                    nodes_addr)
314                 mac_address = adj_int['mac_address']
315                 get_node(node).set_arp(interface_name, ip_address, mac_address)