Revert "API: Cleanup APIs interface.api"
[vpp.git] / test / vpp_interface.py
1 import binascii
2 import socket
3 import abc
4
5 import six
6 from six import moves
7
8 from util import Host, mk_ll_addr
9 from vpp_papi import mac_ntop
10
11
12 @six.add_metaclass(abc.ABCMeta)
13 class VppInterface(object):
14     """Generic VPP interface."""
15
16     @property
17     def sw_if_index(self):
18         """Interface index assigned by VPP."""
19         return self._sw_if_index
20
21     @property
22     def remote_mac(self):
23         """MAC-address of the remote interface "connected" to this interface"""
24         return self._remote_hosts[0].mac
25
26     @property
27     def local_mac(self):
28         """MAC-address of the VPP interface."""
29         return self._local_mac
30
31     @property
32     def local_addr(self):
33         return self._local_addr
34
35     @property
36     def remote_addr(self):
37         return self._remote_addr
38
39     @property
40     def local_addr_n(self):
41         return self._local_addr_n
42
43     @property
44     def remote_addr_n(self):
45         return self._remote_addr_n
46
47     @property
48     def local_ip4(self):
49         """Local IPv4 address on VPP interface (string)."""
50         return self._local_ip4
51
52     @property
53     def local_ip4n(self):
54         """Local IPv4 address - raw, suitable as API parameter."""
55         return socket.inet_pton(socket.AF_INET, self._local_ip4)
56
57     @property
58     def remote_ip4(self):
59         """IPv4 address of remote peer "connected" to this interface."""
60         return self._remote_hosts[0].ip4
61
62     @property
63     def remote_ip4n(self):
64         """IPv4 address of remote peer - raw, suitable as API parameter."""
65         return socket.inet_pton(socket.AF_INET, self.remote_ip4)
66
67     @property
68     def local_ip6(self):
69         """Local IPv6 address on VPP interface (string)."""
70         return self._local_ip6
71
72     @property
73     def local_ip6n(self):
74         """Local IPv6 address - raw, suitable as API parameter."""
75         return socket.inet_pton(socket.AF_INET6, self.local_ip6)
76
77     @property
78     def remote_ip6(self):
79         """IPv6 address of remote peer "connected" to this interface."""
80         return self._remote_hosts[0].ip6
81
82     @property
83     def remote_ip6n(self):
84         """IPv6 address of remote peer - raw, suitable as API parameter"""
85         return socket.inet_pton(socket.AF_INET6, self.remote_ip6)
86
87     @property
88     def local_ip6_ll(self):
89         """Local IPv6 link-local address on VPP interface (string)."""
90         return self._local_ip6_ll
91
92     @property
93     def local_ip6n_ll(self):
94         """Local IPv6 link-local address - raw, suitable as API parameter."""
95         return self._local_ip6n_ll
96
97     @property
98     def remote_ip6_ll(self):
99         """Link-local IPv6 address of remote peer
100         "connected" to this interface."""
101         return self._remote_ip6_ll
102
103     @property
104     def remote_ip6n_ll(self):
105         """Link-local IPv6 address of remote peer
106         - raw, suitable as API parameter"""
107         return self._remote_ip6n_ll
108
109     @property
110     def name(self):
111         """Name of the interface."""
112         return self._name
113
114     @property
115     def dump(self):
116         """RAW result of sw_interface_dump for this interface."""
117         return self._dump
118
119     @property
120     def test(self):
121         """Test case creating this interface."""
122         return self._test
123
124     @property
125     def remote_hosts(self):
126         """Remote hosts list"""
127         return self._remote_hosts
128
129     @remote_hosts.setter
130     def remote_hosts(self, value):
131         """
132         :param list value: List of remote hosts.
133         """
134         self._remote_hosts = value
135         self._hosts_by_mac = {}
136         self._hosts_by_ip4 = {}
137         self._hosts_by_ip6 = {}
138         for host in self._remote_hosts:
139             self._hosts_by_mac[host.mac] = host
140             self._hosts_by_ip4[host.ip4] = host
141             self._hosts_by_ip6[host.ip6] = host
142
143     def host_by_mac(self, mac):
144         """
145         :param mac: MAC address to find host by.
146         :return: Host object assigned to interface.
147         """
148         return self._hosts_by_mac[mac]
149
150     def host_by_ip4(self, ip):
151         """
152         :param ip: IPv4 address to find host by.
153         :return: Host object assigned to interface.
154         """
155         return self._hosts_by_ip4[ip]
156
157     def host_by_ip6(self, ip):
158         """
159         :param ip: IPv6 address to find host by.
160         :return: Host object assigned to interface.
161         """
162         return self._hosts_by_ip6[ip]
163
164     def generate_remote_hosts(self, count=1):
165         """Generate and add remote hosts for the interface.
166
167         :param int count: Number of generated remote hosts.
168         """
169         self._remote_hosts = []
170         self._hosts_by_mac = {}
171         self._hosts_by_ip4 = {}
172         self._hosts_by_ip6 = {}
173         for i in range(
174                 2, count + 2):  # 0: network address, 1: local vpp address
175             mac = "02:%02x:00:00:ff:%02x" % (self.sw_if_index, i)
176             ip4 = "172.16.%u.%u" % (self.sw_if_index, i)
177             ip6 = "fd01:%x::%x" % (self.sw_if_index, i)
178             ip6_ll = mk_ll_addr(mac)
179             host = Host(mac, ip4, ip6, ip6_ll)
180             self._remote_hosts.append(host)
181             self._hosts_by_mac[mac] = host
182             self._hosts_by_ip4[ip4] = host
183             self._hosts_by_ip6[ip6] = host
184
185     @abc.abstractmethod
186     def __init__(self, test):
187         self._test = test
188
189         self._remote_hosts = []
190         self._hosts_by_mac = {}
191         self._hosts_by_ip4 = {}
192         self._hosts_by_ip6 = {}
193
194     def set_mac(self, mac):
195         self._local_mac = str(mac)
196         self._local_ip6_ll = mk_ll_addr(self._local_mac)
197         self.test.vapi.sw_interface_set_mac_address(
198             self.sw_if_index, mac.packed)
199
200     def set_sw_if_index(self, sw_if_index):
201         self._sw_if_index = sw_if_index
202
203         self.generate_remote_hosts()
204
205         self._local_ip4 = "172.16.%u.1" % self.sw_if_index
206         self._local_ip4n = socket.inet_pton(socket.AF_INET, self.local_ip4)
207         self._local_ip4_subnet = "172.16.%u.0" % self.sw_if_index
208         self._local_ip4n_subnet = socket.inet_pton(socket.AF_INET,
209                                                    self._local_ip4_subnet)
210         self._local_ip4_bcast = "172.16.%u.255" % self.sw_if_index
211         self._local_ip4n_bcast = socket.inet_pton(socket.AF_INET,
212                                                   self._local_ip4_bcast)
213         self.local_ip4_prefix_len = 24
214         self.has_ip4_config = False
215         self.ip4_table_id = 0
216
217         self._local_ip6 = "fd01:%x::1" % self.sw_if_index
218         self._local_ip6n = socket.inet_pton(socket.AF_INET6, self.local_ip6)
219         self.local_ip6_prefix_len = 64
220         self.has_ip6_config = False
221         self.ip6_table_id = 0
222
223         self._local_addr = {socket.AF_INET: self.local_ip4,
224                             socket.AF_INET6: self.local_ip6}
225         self._local_addr_n = {socket.AF_INET: self.local_ip4n,
226                               socket.AF_INET6: self.local_ip6n}
227         self._remote_addr = {socket.AF_INET: self.remote_ip4,
228                              socket.AF_INET6: self.remote_ip6}
229         self._remote_addr_n = {socket.AF_INET: self.remote_ip4n,
230                                socket.AF_INET6: self.remote_ip6n}
231
232         r = self.test.vapi.sw_interface_dump()
233         for intf in r:
234             if intf.sw_if_index == self.sw_if_index:
235                 self._name = intf.interface_name.split(b'\0',
236                                                        1)[0].decode('utf8')
237                 self._local_mac = mac_ntop(intf.l2_address)
238                 self._dump = intf
239                 break
240         else:
241             raise Exception(
242                 "Could not find interface with sw_if_index %d "
243                 "in interface dump %s" %
244                 (self.sw_if_index, moves.reprlib.repr(r)))
245         self._local_ip6_ll = mk_ll_addr(self.local_mac)
246         self._local_ip6n_ll = socket.inet_pton(socket.AF_INET6,
247                                                self.local_ip6_ll)
248         self._remote_ip6_ll = mk_ll_addr(self.remote_mac)
249         self._remote_ip6n_ll = socket.inet_pton(socket.AF_INET6,
250                                                 self.remote_ip6_ll)
251
252     def config_ip4(self):
253         """Configure IPv4 address on the VPP interface."""
254         self.test.vapi.sw_interface_add_del_address(
255             sw_if_index=self.sw_if_index, address=self.local_ip4n,
256             address_length=self.local_ip4_prefix_len)
257         self.has_ip4_config = True
258
259     def unconfig_ip4(self):
260         """Remove IPv4 address on the VPP interface."""
261         try:
262             if self.has_ip4_config:
263                 self.test.vapi.sw_interface_add_del_address(
264                     sw_if_index=self.sw_if_index, address=self.local_ip4n,
265                     address_length=self.local_ip4_prefix_len, is_add=0)
266         except AttributeError:
267             self.has_ip4_config = False
268         self.has_ip4_config = False
269
270     def configure_ipv4_neighbors(self):
271         """For every remote host assign neighbor's MAC to IPv4 addresses.
272
273         :param vrf_id: The FIB table / VRF ID. (Default value = 0)
274         """
275         for host in self._remote_hosts:
276             self.test.vapi.ip_neighbor_add_del(self.sw_if_index,
277                                                host.mac,
278                                                host.ip4)
279
280     def config_ip6(self):
281         """Configure IPv6 address on the VPP interface."""
282         self.test.vapi.sw_interface_add_del_address(
283             sw_if_index=self.sw_if_index, address=self._local_ip6n,
284             address_length=self.local_ip6_prefix_len, is_ipv6=1)
285         self.has_ip6_config = True
286
287     def unconfig_ip6(self):
288         """Remove IPv6 address on the VPP interface."""
289         try:
290             if self.has_ip6_config:
291                 self.test.vapi.sw_interface_add_del_address(
292                     sw_if_index=self.sw_if_index, address=self.local_ip6n,
293                     address_length=self.local_ip6_prefix_len, is_ipv6=1,
294                     is_add=0)
295         except AttributeError:
296             self.has_ip6_config = False
297         self.has_ip6_config = False
298
299     def configure_ipv6_neighbors(self):
300         """For every remote host assign neighbor's MAC to IPv6 addresses.
301
302         :param vrf_id: The FIB table / VRF ID. (Default value = 0)
303         """
304         for host in self._remote_hosts:
305             self.test.vapi.ip_neighbor_add_del(self.sw_if_index,
306                                                host.mac,
307                                                host.ip6)
308
309     def unconfig(self):
310         """Unconfigure IPv6 and IPv4 address on the VPP interface."""
311         self.unconfig_ip4()
312         self.unconfig_ip6()
313
314     def set_table_ip4(self, table_id):
315         """Set the interface in a IPv4 Table.
316
317         .. note:: Must be called before configuring IP4 addresses.
318         """
319         self.ip4_table_id = table_id
320         self.test.vapi.sw_interface_set_table(
321             self.sw_if_index, 0, self.ip4_table_id)
322
323     def set_table_ip6(self, table_id):
324         """Set the interface in a IPv6 Table.
325
326         .. note:: Must be called before configuring IP6 addresses.
327         """
328         self.ip6_table_id = table_id
329         self.test.vapi.sw_interface_set_table(
330             self.sw_if_index, 1, self.ip6_table_id)
331
332     def disable_ipv6_ra(self):
333         """Configure IPv6 RA suppress on the VPP interface."""
334         self.test.vapi.sw_interface_ip6nd_ra_config(
335             sw_if_index=self.sw_if_index,
336             suppress=1)
337
338     def ip6_ra_config(self, no=0, suppress=0, send_unicast=0):
339         """Configure IPv6 RA suppress on the VPP interface."""
340         self.test.vapi.sw_interface_ip6nd_ra_config(
341             sw_if_index=self.sw_if_index,
342             is_no=no,
343             suppress=suppress,
344             send_unicast=send_unicast)
345
346     # TODO: This should accept ipaddress object.
347     def ip6_ra_prefix(self, address, address_length, is_no=0,
348                       off_link=0, no_autoconfig=0, use_default=0):
349         """Configure IPv6 RA suppress on the VPP interface."""
350         self.test.vapi.sw_interface_ip6nd_ra_prefix(
351             sw_if_index=self.sw_if_index,
352             prefix={'address': address,
353                     'address_length': address_length},
354             use_default=use_default,
355             off_link=off_link, no_autoconfig=no_autoconfig,
356             is_no=is_no)
357
358     def admin_up(self):
359         """Put interface ADMIN-UP."""
360         self.test.vapi.sw_interface_set_flags(self.sw_if_index,
361                                               admin_up_down=1)
362
363     def admin_down(self):
364         """Put interface ADMIN-down."""
365         self.test.vapi.sw_interface_set_flags(self.sw_if_index,
366                                               admin_up_down=0)
367
368     def link_up(self):
369         """Put interface link-state-UP."""
370         self.test.vapi.cli("test interface link-state %s up" % self.name)
371
372     def link_down(self):
373         """Put interface link-state-down."""
374         self.test.vapi.cli("test interface link-state %s down" % self.name)
375
376     def ip6_enable(self):
377         """IPv6 Enable interface"""
378         self.test.vapi.sw_interface_ip6_enable_disable(self.sw_if_index,
379                                                        enable=1)
380
381     def ip6_disable(self):
382         """Put interface ADMIN-DOWN."""
383         self.test.vapi.sw_interface_ip6_enable_disable(self.sw_if_index,
384                                                        enable=0)
385
386     def add_sub_if(self, sub_if):
387         """Register a sub-interface with this interface.
388
389         :param sub_if: sub-interface
390         """
391         if not hasattr(self, 'sub_if'):
392             self.sub_if = sub_if
393         else:
394             if isinstance(self.sub_if, list):
395                 self.sub_if.append(sub_if)
396             else:
397                 self.sub_if = sub_if
398
399     def enable_mpls(self):
400         """Enable MPLS on the VPP interface."""
401         self.test.vapi.sw_interface_set_mpls_enable(self.sw_if_index)
402
403     def disable_mpls(self):
404         """Enable MPLS on the VPP interface."""
405         self.test.vapi.sw_interface_set_mpls_enable(self.sw_if_index, 0)
406
407     def is_ip4_entry_in_fib_dump(self, dump):
408         for i in dump:
409             if i.address == self.local_ip4n and \
410                     i.address_length == self.local_ip4_prefix_len and \
411                     i.table_id == self.ip4_table_id:
412                 return True
413         return False
414
415     def set_unnumbered(self, ip_sw_if_index):
416         """ Set the interface to unnumbered via ip_sw_if_index """
417         self.test.vapi.sw_interface_set_unnumbered(ip_sw_if_index,
418                                                    self.sw_if_index)
419
420     def unset_unnumbered(self, ip_sw_if_index):
421         """ Unset the interface to unnumbered via ip_sw_if_index """
422         self.test.vapi.sw_interface_set_unnumbered(ip_sw_if_index,
423                                                    self.sw_if_index, is_add=0)
424
425     def set_proxy_arp(self, enable=1):
426         """ Set the interface to enable/disable Proxy ARP """
427         self.test.vapi.proxy_arp_intfc_enable_disable(
428             self.sw_if_index,
429             enable)
430
431     def query_vpp_config(self):
432         dump = self.test.vapi.sw_interface_dump()
433         return self.is_interface_config_in_dump(dump)
434
435     def get_interface_config_from_dump(self, dump):
436         for i in dump:
437             if i.interface_name.rstrip(' \t\r\n\0') == self.name and \
438                     i.sw_if_index == self.sw_if_index:
439                 return i
440         else:
441             return None
442
443     def is_interface_config_in_dump(self, dump):
444         return self.get_interface_config_from_dump(dump) is not None
445
446     def assert_interface_state(self, admin_up_down, link_up_down,
447                                expect_event=False):
448         if expect_event:
449             event = self.test.vapi.wait_for_event(timeout=1,
450                                                   name='sw_interface_event')
451             self.test.assert_equal(event.sw_if_index, self.sw_if_index,
452                                    "sw_if_index")
453             self.test.assert_equal(event.admin_up_down, admin_up_down,
454                                    "admin state")
455             self.test.assert_equal(event.link_up_down, link_up_down,
456                                    "link state")
457         dump = self.test.vapi.sw_interface_dump()
458         if_state = self.get_interface_config_from_dump(dump)
459         self.test.assert_equal(if_state.admin_up_down, admin_up_down,
460                                "admin state")
461         self.test.assert_equal(if_state.link_up_down, link_up_down,
462                                "link state")
463
464     def __str__(self):
465         return self.name