Revert "CSIT-986: Use MLRsearch from pip"
[csit.git] / resources / libraries / python / IPUtil.py
1 # Copyright (c) 2018 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 """Common IP utilities library."""
15
16 import re
17
18 from ipaddress import IPv4Network, ip_address
19
20 from resources.libraries.python.ssh import SSH
21 from resources.libraries.python.constants import Constants
22 from resources.libraries.python.ssh import exec_cmd_no_error, exec_cmd
23 from resources.libraries.python.topology import Topology
24
25
26 class IPUtil(object):
27     """Common IP utilities"""
28
29     @staticmethod
30     def ip_to_int(ip_str):
31         """Convert IP address from string format (e.g. 10.0.0.1) to integer
32         representation (167772161).
33
34         :param ip_str: IP address in string representation.
35         :type ip_str: str
36         :returns: Integer representation of IP address.
37         :rtype: int
38         """
39         return int(ip_address(unicode(ip_str)))
40
41     @staticmethod
42     def int_to_ip(ip_int):
43         """Convert IP address from integer representation (e.g. 167772161) to
44         string format (10.0.0.1).
45
46         :param ip_int: IP address in integer representation.
47         :type ip_int: int
48         :returns: String representation of IP address.
49         :rtype: str
50         """
51         return str(ip_address(ip_int))
52
53     @staticmethod
54     def vpp_ip_probe(node, interface, addr, if_type="key"):
55         """Run ip probe on VPP node.
56
57         :param node: VPP node.
58         :param interface: Interface key or name.
59         :param addr: IPv4/IPv6 address.
60         :param if_type: Interface type
61         :type node: dict
62         :type interface: str
63         :type addr: str
64         :type if_type: str
65         :raises ValueError: If the if_type is unknown.
66         :raises Exception: If vpp probe fails.
67         """
68         ssh = SSH()
69         ssh.connect(node)
70
71         if if_type == "key":
72             iface_name = Topology.get_interface_name(node, interface)
73         elif if_type == "name":
74             iface_name = interface
75         else:
76             raise ValueError("if_type unknown: {0}".format(if_type))
77
78         cmd = "{c}".format(c=Constants.VAT_BIN_NAME)
79         cmd_input = 'exec ip probe {dev} {ip}'.format(dev=iface_name, ip=addr)
80         (ret_code, _, _) = ssh.exec_command_sudo(cmd, cmd_input)
81         if int(ret_code) != 0:
82             raise Exception('VPP ip probe {dev} {ip} failed on {h}'.format(
83                 dev=iface_name, ip=addr, h=node['host']))
84
85     @staticmethod
86     def ip_addresses_should_be_equal(ip1, ip2):
87         """Fails if the given IP addresses are unequal.
88
89         :param ip1: IPv4 or IPv6 address.
90         :param ip2: IPv4 or IPv6 address.
91         :type ip1: str
92         :type ip2: str
93         """
94
95         addr1 = ip_address(unicode(ip1))
96         addr2 = ip_address(unicode(ip2))
97
98         if addr1 != addr2:
99             raise AssertionError('IP addresses are not equal: {0} != {1}'.
100                                  format(ip1, ip2))
101
102     @staticmethod
103     def setup_network_namespace(node, namespace_name, interface_name,
104                                 ip_addr, prefix):
105         """Setup namespace on given node and attach interface and IP to
106         this namespace. Applicable also on TG node.
107
108         :param node: Node to set namespace on.
109         :param namespace_name: Namespace name.
110         :param interface_name: Interface name.
111         :param ip_addr: IP address of namespace's interface.
112         :param prefix: IP address prefix length.
113         :type node: dict
114         :type namespace_name: str
115         :type vhost_if: str
116         :type ip_addr: str
117         :type prefix: int
118         """
119         cmd = ('ip netns add {0}'.format(namespace_name))
120         exec_cmd_no_error(node, cmd, sudo=True)
121
122         cmd = ('ip link set dev {0} up netns {1}'.format(interface_name,
123                                                          namespace_name))
124         exec_cmd_no_error(node, cmd, sudo=True)
125
126         cmd = ('ip netns exec {0} ip addr add {1}/{2} dev {3}'.format(
127             namespace_name, ip_addr, prefix, interface_name))
128         exec_cmd_no_error(node, cmd, sudo=True)
129
130     @staticmethod
131     def linux_enable_forwarding(node, ip_ver='ipv4'):
132         """Enable forwarding on a Linux node, e.g. VM.
133
134         :param node: Node to enable forwarding on.
135         :param ip_ver: IP version, 'ipv4' or 'ipv6'.
136         :type node: dict
137         :type ip_ver: str
138         """
139         cmd = 'sysctl -w net.{0}.ip_forward=1'.format(ip_ver)
140         exec_cmd_no_error(node, cmd, sudo=True)
141
142     @staticmethod
143     def get_linux_interface_name(node, pci_addr):
144         """Get the interface name.
145
146         :param node: Node where to execute command.
147         :param pci_addr: PCI address
148         :type node: dict
149         :type pci_addr: str
150         :returns: Interface name
151         :rtype: str
152         :raises RuntimeError: If cannot get the information about interfaces.
153         """
154
155         regex_intf_info = r"pci@" \
156                           r"([0-9a-f]{4}:[0-9a-f]{2}:[0-9a-f]{2}.[0-9a-f])\s*" \
157                           r"([a-zA-Z0-9]*)\s*network"
158
159         cmd = "lshw -class network -businfo"
160         ret_code, stdout, stderr = exec_cmd(node, cmd, timeout=30, sudo=True)
161         if ret_code != 0:
162             raise RuntimeError('Could not get information about interfaces, '
163                                'reason:{0}'.format(stderr))
164
165         for line in stdout.splitlines()[2:]:
166             try:
167                 if re.search(regex_intf_info, line).group(1) == pci_addr:
168                     return re.search(regex_intf_info, line).group(2)
169             except AttributeError:
170                 continue
171         return None
172
173     @staticmethod
174     def set_linux_interface_up(node, interface):
175         """Set the specified interface up.
176
177         :param node: Node where to execute command.
178         :param interface: Interface in namespace.
179         :type node: dict
180         :type interface: str
181         :raises RuntimeError: If the interface could not be set up.
182         """
183
184         cmd = "ip link set {0} up".format(interface)
185         ret_code, _, stderr = exec_cmd(node, cmd, timeout=30, sudo=True)
186         if ret_code != 0:
187             raise RuntimeError('Could not set the interface up, reason:{0}'.
188                                format(stderr))
189
190     @staticmethod
191     def set_linux_interface_ip(node, interface, ip_addr, prefix,
192                                namespace=None):
193         """Set IP address to interface in linux.
194
195         :param node: Node where to execute command.
196         :param interface: Interface in namespace.
197         :param ip_addr: IP to be set on interface.
198         :param prefix: IP prefix.
199         :param namespace: Execute command in namespace. Optional
200         :type node: dict
201         :type interface: str
202         :type ip_addr: str
203         :type prefix: int
204         :type namespace: str
205         :raises RuntimeError: IP could not be set.
206         """
207         if namespace is not None:
208             cmd = 'ip netns exec {} ip addr add {}/{} dev {}'.format(
209                 namespace, ip_addr, prefix, interface)
210         else:
211             cmd = 'ip addr add {}/{} dev {}'.format(ip_addr, prefix, interface)
212         (ret_code, _, stderr) = exec_cmd(node, cmd, timeout=5, sudo=True)
213         if ret_code != 0:
214             raise RuntimeError(
215                 'Could not set IP for interface, reason:{}'.format(stderr))
216
217     @staticmethod
218     def set_linux_interface_route(node, interface, route, namespace=None):
219         """Set route via interface in linux.
220
221         :param node: Node where to execute command.
222         :param interface: Interface in namespace.
223         :param route: Route to be added via interface.
224         :param namespace: Execute command in namespace. Optional parameter.
225         :type node: dict
226         :type interface: str
227         :type route: str
228         :type namespace: str
229         """
230         if namespace is not None:
231             cmd = 'ip netns exec {} ip route add {} dev {}'.format(
232                 namespace, route, interface)
233         else:
234             cmd = 'ip route add {} dev {}'.format(route, interface)
235         exec_cmd_no_error(node, cmd, sudo=True)
236
237
238 def convert_ipv4_netmask_prefix(network):
239     """Convert network mask to equivalent network prefix length or vice versa.
240
241     Example: mask 255.255.0.0 -> prefix length 16
242     :param network: Network mask or network prefix length.
243     :type network: str or int
244     :returns: Network mask or network prefix length.
245     :rtype: str or int
246     """
247     temp_address = "0.0.0.0"
248     net = IPv4Network(u"{0}/{1}".format(temp_address, network), False)
249
250     if isinstance(network, int) and (0 < network < 33):
251         return str(net.netmask)
252     elif isinstance(network, basestring):
253         return int(net.prefixlen)
254     else:
255         raise Exception("Value {0} is not a valid ipv4 netmask or network"
256                         " prefix length".format(network))