09c9ae9a3b32108bc8bf11355cd23fb84ad37bf7
[csit.git] / resources / libraries / python / honeycomb / HcAPIKwInterfaces.py
1 # Copyright (c) 2017 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 """Keywords to manipulate interface configuration using Honeycomb REST API.
15
16 The keywords make possible to put and get configuration data and to get
17 operational data.
18 """
19 from robot.api import logger
20
21 from resources.libraries.python.topology import Topology
22 from resources.libraries.python.HTTPRequest import HTTPCodes
23 from resources.libraries.python.honeycomb.HoneycombSetup import HoneycombError
24 from resources.libraries.python.honeycomb.HoneycombUtil \
25     import DataRepresentation
26 from resources.libraries.python.honeycomb.HoneycombUtil \
27     import HoneycombUtil as HcUtil
28
29
30 class InterfaceKeywords(object):
31     """Keywords for Interface manipulation.
32
33     Implements keywords which get configuration and operational data about
34     vpp interfaces and set the interface's parameters using Honeycomb REST API.
35     """
36
37     INTF_PARAMS = ("name", "description", "type", "enabled",
38                    "link-up-down-trap-enable", "v3po:l2", "v3po:vxlan-gpe",
39                    "vpp-vlan:sub-interfaces")
40     IPV4_PARAMS = ("enabled", "forwarding", "mtu")
41     IPV6_PARAMS = ("enabled", "forwarding", "mtu", "dup-addr-detect-transmits")
42     IPV6_AUTOCONF_PARAMS = ("create-global-addresses",
43                             "create-temporary-addresses",
44                             "temporary-valid-lifetime",
45                             "temporary-preferred-lifetime")
46     ETH_PARAMS = ("mtu", )
47     ROUTING_PARAMS = ("ipv4-vrf-id", "ipv6-vrf-id")
48     VXLAN_PARAMS = ("src", "dst", "vni", "encap-vrf-id")
49     L2_PARAMS = ("bridge-domain", "split-horizon-group",
50                  "bridged-virtual-interface")
51     TAP_PARAMS = ("tap-name", "mac", "device-instance")
52     VHOST_USER_PARAMS = ("socket", "role")
53     SUB_IF_PARAMS = ("identifier",
54                      "vlan-type",
55                      "enabled")
56     SUB_IF_MATCH = ("default",
57                     "untagged",
58                     "vlan-tagged",
59                     "vlan-tagged-exact-match")
60     BD_PARAMS = ("bridge-domain",
61                  "split-horizon-group",
62                  "bridged-virtual-interface")
63     VXLAN_GPE_PARAMS = ("local",
64                         "remote",
65                         "vni",
66                         "next-protocol",
67                         "encap-vrf-id",
68                         "decap-vrf-id")
69
70     def __init__(self):
71         pass
72
73     @staticmethod
74     def _configure_interface(node, interface, data,
75                              data_representation=DataRepresentation.JSON):
76         """Send interface configuration data and check the response.
77
78         :param node: Honeycomb node.
79         :param interface: The name of interface.
80         :param data: Configuration data to be sent in PUT request.
81         :param data_representation: How the data is represented.
82         :type node: dict
83         :type interface: str
84         :type data: dict
85         :type data_representation: DataRepresentation
86         :returns: Content of response.
87         :rtype: bytearray
88         :raises HoneycombError: If the status code in response on PUT is not
89         200 = OK.
90         """
91
92         status_code, resp = HcUtil.\
93             put_honeycomb_data(node, "config_vpp_interfaces", data,
94                                data_representation=data_representation)
95         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
96             raise HoneycombError(
97                 "The configuration of interface '{0}' was not successful. "
98                 "Status code: {1}.".format(interface, status_code))
99         return resp
100
101     @staticmethod
102     def get_all_interfaces_cfg_data(node):
103         """Get configuration data about all interfaces from Honeycomb.
104
105         :param node: Honeycomb node.
106         :type node: dict
107         :returns: Configuration data about all interfaces from Honeycomb.
108         :rtype: list
109         :raises HoneycombError: If it is not possible to get configuration data.
110         """
111
112         status_code, resp = HcUtil.\
113             get_honeycomb_data(node, "config_vpp_interfaces")
114         if status_code != HTTPCodes.OK:
115             raise HoneycombError(
116                 "Not possible to get configuration information about the "
117                 "interfaces. Status code: {0}.".format(status_code))
118         try:
119             return resp["interfaces"]["interface"]
120
121         except (KeyError, TypeError):
122             return []
123
124     @staticmethod
125     def get_interface_cfg_data(node, interface):
126         """Get configuration data about the given interface from Honeycomb.
127
128         :param node: Honeycomb node.
129         :param interface: The name of interface.
130         :type node: dict
131         :type interface: str
132         :returns: Configuration data about the given interface from Honeycomb.
133         :rtype: dict
134         """
135
136         intfs = InterfaceKeywords.get_all_interfaces_cfg_data(node)
137         for intf in intfs:
138             if intf["name"] == interface:
139                 return intf
140         return {}
141
142     @staticmethod
143     def get_all_interfaces_oper_data(node):
144         """Get operational data about all interfaces from Honeycomb.
145
146         :param node: Honeycomb node.
147         :type node: dict
148         :returns: Operational data about all interfaces from Honeycomb.
149         :rtype: list
150         :raises HoneycombError: If it is not possible to get operational data.
151         """
152
153         status_code, resp = HcUtil.\
154             get_honeycomb_data(node, "oper_vpp_interfaces")
155         if status_code != HTTPCodes.OK:
156             raise HoneycombError(
157                 "Not possible to get operational information about the "
158                 "interfaces. Status code: {0}.".format(status_code))
159         try:
160             return resp["interfaces-state"]["interface"]
161
162         except (KeyError, TypeError):
163             return []
164
165     @staticmethod
166     def get_disabled_interfaces_oper_data(node):
167         """Get operational data about all disabled interfaces from Honeycomb.
168
169         :param node: Honeycomb node.
170         :type node: dict
171         :returns: Operational data about disabled interfaces.
172         :rtype: list
173         :raises HoneycombError: If it is not possible to get operational data.
174         """
175
176         status_code, resp = HcUtil. \
177             get_honeycomb_data(node, "oper_disabled_interfaces")
178         if status_code == HTTPCodes.NOT_FOUND:
179             raise HoneycombError(
180                 "No disabled interfaces present on node."
181             )
182         if status_code != HTTPCodes.OK:
183             raise HoneycombError(
184                 "Not possible to get operational information about the "
185                 "interfaces. Status code: {0}.".format(status_code))
186         try:
187             return resp["disabled-interfaces"]["disabled-interface-index"]
188
189         except (KeyError, TypeError):
190             return []
191
192     @staticmethod
193     def get_interface_oper_data(node, interface):
194         """Get operational data about the given interface from Honeycomb.
195
196         :param node: Honeycomb node.
197         :param interface: The name of interface.
198         :type node: dict
199         :type interface: str
200         :returns: Operational data about the given interface from Honeycomb.
201         :rtype: dict
202         """
203
204         try:
205             interface = Topology.convert_interface_reference(
206                 node, interface, "name")
207         except RuntimeError:
208             if isinstance(interface, basestring):
209                 # Probably name of a custom interface (TAP, VxLAN, Vhost, ...)
210                 pass
211             else:
212                 raise
213
214         intfs = InterfaceKeywords.get_all_interfaces_oper_data(node)
215         for intf in intfs:
216             if intf["name"] == interface:
217                 return intf
218         return {}
219
220     @staticmethod
221     def _set_interface_properties(node, interface, path, new_value=None):
222         """Set interface properties.
223
224         This method reads interface configuration data, creates, changes or
225         removes the requested data and puts it back to Honeycomb.
226
227         :param node: Honeycomb node.
228         :param interface: The name of interface.
229         :param path:  Path to data we want to change / create / remove.
230         :param new_value: The new value to be set. If None, the item will be
231         removed.
232         :type node: dict
233         :type interface: str
234         :type path: tuple
235         :type new_value: str, dict or list
236         :returns: Content of response.
237         :rtype: bytearray
238         :raises HoneycombError: If it is not possible to get or set the data.
239         """
240
241         status_code, resp = HcUtil.\
242             get_honeycomb_data(node, "config_vpp_interfaces")
243         if status_code != HTTPCodes.OK:
244             raise HoneycombError(
245                 "Not possible to get configuration information about the "
246                 "interfaces. Status code: {0}.".format(status_code))
247
248         if new_value:
249             new_data = HcUtil.set_item_value(resp, path, new_value)
250         else:
251             new_data = HcUtil.remove_item(resp, path)
252         return InterfaceKeywords._configure_interface(node, interface, new_data)
253
254     @staticmethod
255     def honeycomb_set_interface_state(node, interface, state="up"):
256         """Set VPP interface state.
257
258         The keyword changes the administration state of interface to up or down
259         depending on the parameter "state".
260
261         :param node: Honeycomb node.
262         :param interface: Interface name, key, link name or sw_if_index.
263         :param state: The requested state, only "up" and "down" are valid
264         values.
265         :type node: dict
266         :type interface: str
267         :type state: str
268         :returns: Content of response.
269         :rtype: bytearray
270         :raises KeyError: If the argument "state" is nor "up" or "down".
271         :raises HoneycombError: If the interface is not present on the node.
272         """
273
274         intf_state = {"up": "true",
275                       "down": "false"}
276
277         interface = Topology.convert_interface_reference(
278             node, interface, "name")
279
280         intf = interface.replace("/", "%2F")
281         path = "/interface/{0}".format(intf)
282
283         status_code, resp = HcUtil.\
284             get_honeycomb_data(node, "config_vpp_interfaces", path)
285         if status_code != HTTPCodes.OK:
286             raise HoneycombError(
287                 "Not possible to get configuration information about the "
288                 "interfaces. Status code: {0}.".format(status_code))
289
290         resp["interface"][0]["enabled"] = intf_state[state.lower()]
291
292         status_code, resp = HcUtil. \
293             put_honeycomb_data(node, "config_vpp_interfaces", resp, path,
294                                data_representation=DataRepresentation.JSON)
295         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
296             raise HoneycombError(
297                 "The configuration of interface '{0}' was not successful. "
298                 "Status code: {1}.".format(interface, status_code))
299         return resp
300
301     @staticmethod
302     def set_interface_up(node, interface):
303         """Set the administration state of VPP interface to up.
304
305         :param node: Honeycomb node.
306         :param interface: The name of interface.
307         :type node: dict
308         :type interface: str
309         :returns: Content of response
310         :rtype: bytearray
311         """
312
313         return InterfaceKeywords.honeycomb_set_interface_state(
314             node, interface, "up")
315
316     @staticmethod
317     def set_interface_down(node, interface):
318         """Set the administration state of VPP interface to down.
319
320         :param node: Honeycomb node.
321         :param interface: The name of interface.
322         :type node: dict
323         :type interface: str
324         :returns: Content of response.
325         :rtype: bytearray
326         """
327
328         return InterfaceKeywords.honeycomb_set_interface_state(
329             node, interface, "down")
330
331     @staticmethod
332     def add_bridge_domain_to_interface(node, interface, bd_name,
333                                        split_horizon_group=None, bvi=None):
334         """Add a new bridge domain to an interface and set its parameters.
335
336         :param node: Honeycomb node.
337         :param interface: Interface name, key, link name or sw_if_index.
338         :param bd_name: Bridge domain name.
339         :param split_horizon_group: Split-horizon group name.
340         :param bvi: The bridged virtual interface.
341         :type node: dict
342         :type interface: str
343         :type bd_name: str
344         :type split_horizon_group: str
345         :type bvi: str
346         :returns: Content of response.
347         :rtype: bytearray
348         :raises HoneycombError: If the interface is not present on the node.
349         """
350
351         interface = Topology.convert_interface_reference(
352             node, interface, "name")
353
354         v3po_l2 = {"bridge-domain": str(bd_name)}
355         if split_horizon_group:
356             v3po_l2["split-horizon-group"] = str(split_horizon_group)
357         if bvi:
358             v3po_l2["bridged-virtual-interface"] = str(bvi)
359
360         path = ("interfaces", ("interface", "name", str(interface)), "v3po:l2")
361
362         return InterfaceKeywords._set_interface_properties(
363             node, interface, path, v3po_l2)
364
365     @staticmethod
366     def remove_bridge_domain_from_interface(node, interface):
367         """Remove bridge domain assignment from interface.
368
369         :param node: Honeycomb node.
370         :param interface: Interface name, key, link name or sw_if_index.
371         :type node: dict
372         :type interface: str or int
373
374         :raises HoneycombError: If the operation fails.
375         """
376
377         interface = Topology.convert_interface_reference(
378             node, interface, "name")
379
380         intf = interface.replace("/", "%2F")
381
382         path = "/interface/{0}/v3po:l2".format(intf)
383
384         status_code, response = HcUtil.delete_honeycomb_data(
385             node, "config_vpp_interfaces", path)
386
387         if status_code != HTTPCodes.OK:
388             if '"error-tag":"data-missing"' in response:
389                 logger.debug("Data does not exist in path.")
390             else:
391                 raise HoneycombError(
392                     "Could not remove bridge domain assignment from interface "
393                     "'{0}'. Status code: {1}.".format(interface, status_code))
394
395     @staticmethod
396     def get_bd_oper_data_from_interface(node, interface):
397         """Returns operational data about bridge domain settings in the
398         interface.
399
400         :param node: Honeycomb node.
401         :param interface: The name of interface.
402         :type node: dict
403         :type interface: str
404         :returns: Operational data about bridge domain settings in the
405         interface.
406         :rtype: dict
407         """
408
409         if_data = InterfaceKeywords.get_interface_oper_data(node, interface)
410
411         if if_data:
412             try:
413                 return if_data["v3po:l2"]
414             except KeyError:
415                 return {}
416         return {}
417
418     @staticmethod
419     def configure_interface_base(node, interface, param, value):
420         """Configure the base parameters of interface.
421
422         :param node: Honeycomb node.
423         :param interface: The name of interface.
424         :param param: Parameter to configure (set, change, remove)
425         :param value: The value of parameter. If None, the parameter will be
426         removed.
427         :type node: dict
428         :type interface: str
429         :type param: str
430         :type value: str
431         :returns: Content of response.
432         :rtype: bytearray
433         :raises HoneycombError: If the parameter is not valid.
434         """
435
436         if param not in InterfaceKeywords.INTF_PARAMS:
437             raise HoneycombError("The parameter {0} is invalid.".format(param))
438
439         path = ("interfaces", ("interface", "name", interface), param)
440         return InterfaceKeywords._set_interface_properties(
441             node, interface, path, value)
442
443     @staticmethod
444     def configure_interface_ipv4(node, interface, param, value):
445         """Configure IPv4 parameters of interface.
446
447         :param node: Honeycomb node.
448         :param interface: The name of interface.
449         :param param: Parameter to configure (set, change, remove)
450         :param value: The value of parameter. If None, the parameter will be
451         removed.
452         :type node: dict
453         :type interface: str
454         :type param: str
455         :type value: str
456         :returns: Content of response.
457         :rtype: bytearray
458         :raises HoneycombError: If the parameter is not valid.
459         """
460
461         interface = Topology.convert_interface_reference(
462             node, interface, "name")
463
464         if param not in InterfaceKeywords.IPV4_PARAMS:
465             raise HoneycombError("The parameter {0} is invalid.".format(param))
466
467         path = ("interfaces", ("interface", "name", interface),
468                 "ietf-ip:ipv4", param)
469         return InterfaceKeywords._set_interface_properties(
470             node, interface, path, value)
471
472     @staticmethod
473     def add_first_ipv4_address(node, interface, ip_addr, network):
474         """Add the first IPv4 address.
475
476         If there are any other addresses configured, they will be removed.
477
478         :param node: Honeycomb node.
479         :param interface: The name of interface.
480         :param ip_addr: IPv4 address to be set.
481         :param network: Netmask or length of network prefix.
482         :type node: dict
483         :type interface: str
484         :type ip_addr: str
485         :type network: str or int
486         :returns: Content of response.
487         :rtype: bytearray
488         :raises ValueError: If the provided netmask or prefix is not valid.
489         :raises HoneycombError: If the operation fails.
490         """
491
492         interface = InterfaceKeywords.handle_interface_reference(
493             node, interface)
494
495         path = "/interface/{0}/ietf-ip:ipv4".format(interface)
496         if isinstance(network, basestring):
497             data = {
498                 "ietf-ip:ipv4": {
499                     "address": [{"ip": ip_addr, "netmask": network}, ]}}
500         elif isinstance(network, int) and (0 < network < 33):
501             data = {
502                 "ietf-ip:ipv4": {
503                     "address": [{"ip": ip_addr, "prefix-length": network}, ]}}
504         else:
505             raise ValueError("Value {0} is not a valid netmask or network "
506                              "prefix length.".format(network))
507         status_code, _ = HcUtil.put_honeycomb_data(
508             node, "config_vpp_interfaces", data, path)
509
510         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
511             raise HoneycombError(
512                 "Configuring IPv4 address failed. "
513                 "Status code:{0}".format(status_code))
514
515     @staticmethod
516     def add_ipv4_address(node, interface, ip_addr, network):
517         """Add IPv4 address.
518
519         :param node: Honeycomb node.
520         :param interface: The name of interface.
521         :param ip_addr: IPv4 address to be set.
522         :param network: Netmask or length of network prefix.
523         :type node: dict
524         :type interface: str
525         :type ip_addr: str
526         :type network: str or int
527         :returns: Content of response.
528         :rtype: bytearray
529         :raises HoneycombError: If the provided netmask or prefix is not valid.
530         """
531
532         interface = Topology.convert_interface_reference(
533             node, interface, "name")
534
535         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4",
536                 "address")
537         if isinstance(network, basestring):
538             address = [{"ip": ip_addr, "netmask": network}]
539         elif isinstance(network, int) and (0 < network < 33):
540             address = [{"ip": ip_addr, "prefix-length": network}]
541         else:
542             raise HoneycombError("Value {0} is not a valid netmask or network "
543                                  "prefix length.".format(network))
544         return InterfaceKeywords._set_interface_properties(
545             node, interface, path, address)
546
547     @staticmethod
548     def remove_all_ipv4_addresses(node, interface):
549         """Remove all IPv4 addresses from interface.
550
551         :param node: Honeycomb node.
552         :param interface: The name of interface.
553         :type node: dict
554         :type interface: str
555         :returns: Content of response.
556         :rtype: bytearray
557         """
558
559         interface = Topology.convert_interface_reference(
560             node, interface, "name")
561
562         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4",
563                 "address")
564         return InterfaceKeywords._set_interface_properties(
565             node, interface, path, None)
566
567     @staticmethod
568     def add_ipv4_neighbor(node, interface, ip_addr, link_layer_address):
569         """Add the IPv4 neighbour.
570
571         :param node: Honeycomb node.
572         :param interface: The name of interface.
573         :param ip_addr: IPv4 address of neighbour to be set.
574         :param link_layer_address: Link layer address.
575         :type node: dict
576         :type interface: str
577         :type ip_addr: str
578         :type link_layer_address: str
579         :returns: Content of response.
580         :rtype: bytearray
581         """
582
583         interface = Topology.convert_interface_reference(
584             node, interface, "name")
585
586         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4",
587                 "neighbor")
588         neighbor = [{"ip": ip_addr, "link-layer-address": link_layer_address}, ]
589         return InterfaceKeywords._set_interface_properties(
590             node, interface, path, neighbor)
591
592     @staticmethod
593     def remove_all_ipv4_neighbors(node, interface):
594         """Remove all IPv4 neighbours.
595
596         :param node: Honeycomb node.
597         :param interface: The name of interface.
598         :type node: dict
599         :type interface: str
600         :returns: Content of response.
601         :rtype: bytearray
602         """
603
604         interface = Topology.convert_interface_reference(
605             node, interface, "name")
606
607         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4",
608                 "neighbor")
609         return InterfaceKeywords._set_interface_properties(
610             node, interface, path, None)
611
612     @staticmethod
613     def configure_interface_ipv6(node, interface, param, value):
614         """Configure IPv6 parameters of interface
615
616         :param node: Honeycomb node.
617         :param interface: The name of interface.
618         :param param: Parameter to configure (set, change, remove)
619         :param value: The value of parameter. If None, the parameter will be
620         removed.
621         :type node: dict
622         :type interface: str
623         :type param: str
624         :type value: str
625         :returns: Content of response.
626         :rtype: bytearray
627         :raises HoneycombError: If the parameter is not valid.
628         """
629
630         if param in InterfaceKeywords.IPV6_PARAMS:
631             path = ("interfaces", ("interface", "name", interface),
632                     "ietf-ip:ipv6", param)
633         elif param in InterfaceKeywords.IPV6_AUTOCONF_PARAMS:
634             path = ("interfaces", ("interface", "name", interface),
635                     "ietf-ip:ipv6", "autoconf", param)
636         else:
637             raise HoneycombError("The parameter {0} is invalid.".format(param))
638
639         return InterfaceKeywords._set_interface_properties(
640             node, interface, path, value)
641
642     @staticmethod
643     def add_first_ipv6_address(node, interface, ip_addr, prefix_len):
644         """Add the first IPv6 address.
645
646         If there are any other addresses configured, they will be removed.
647
648         :param node: Honeycomb node.
649         :param interface: The name of interface.
650         :param ip_addr: IPv6 address to be set.
651         :param prefix_len: Prefix length.
652         :type node: dict
653         :type interface: str
654         :type ip_addr: str
655         :type prefix_len: str
656         :returns: Content of response.
657         :rtype: bytearray
658         """
659
660         interface = Topology.convert_interface_reference(
661             node, interface, "name")
662
663         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6")
664         address = {"address": [{"ip": ip_addr, "prefix-length": prefix_len}, ]}
665         return InterfaceKeywords._set_interface_properties(
666             node, interface, path, address)
667
668     @staticmethod
669     def add_ipv6_address(node, interface, ip_addr, prefix_len):
670         """Add IPv6 address.
671
672         :param node: Honeycomb node.
673         :param interface: The name of interface.
674         :param ip_addr: IPv6 address to be set.
675         :param prefix_len: Prefix length.
676         :type node: dict
677         :type interface: str
678         :type ip_addr: str
679         :type prefix_len: str
680         :returns: Content of response.
681         :rtype: bytearray
682         """
683
684         interface = Topology.convert_interface_reference(
685             node, interface, "name")
686
687         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6",
688                 "address")
689         address = [{"ip": ip_addr, "prefix-length": prefix_len}, ]
690         return InterfaceKeywords._set_interface_properties(
691             node, interface, path, address)
692
693     @staticmethod
694     def remove_all_ipv6_addresses(node, interface):
695         """Remove all IPv6 addresses from interface.
696
697         :param node: Honeycomb node.
698         :param interface: The name of interface.
699         :type node: dict
700         :type interface: str
701         :returns: Content of response.
702         :rtype: bytearray
703         """
704
705         interface = Topology.convert_interface_reference(
706             node, interface, "name")
707
708         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6",
709                 "address")
710         return InterfaceKeywords._set_interface_properties(
711             node, interface, path, None)
712
713     @staticmethod
714     def add_ipv6_neighbor(node, interface, ip_addr, link_layer_address):
715         """Add the IPv6 neighbour.
716
717         :param node: Honeycomb node.
718         :param interface: The name of interface.
719         :param ip_addr: IPv6 address of neighbour to be set.
720         :param link_layer_address: Link layer address.
721         :type node: dict
722         :type interface: str
723         :type ip_addr: str
724         :type link_layer_address: str
725         :returns: Content of response.
726         :rtype: bytearray
727         """
728
729         interface = Topology.convert_interface_reference(
730             node, interface, "name")
731
732         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6",
733                 "neighbor")
734         neighbor = [{"ip": ip_addr, "link-layer-address": link_layer_address}, ]
735         return InterfaceKeywords._set_interface_properties(
736             node, interface, path, neighbor)
737
738     @staticmethod
739     def remove_all_ipv6_neighbors(node, interface):
740         """Remove all IPv6 neighbours.
741
742         :param node: Honeycomb node.
743         :param interface: The name of interface.
744         :type node: dict
745         :type interface: str
746         :returns: Content of response.
747         :rtype: bytearray
748         """
749
750         interface = Topology.convert_interface_reference(
751             node, interface, "name")
752
753         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6",
754                 "neighbor")
755         return InterfaceKeywords._set_interface_properties(
756             node, interface, path, None)
757
758     @staticmethod
759     def configure_interface_ethernet(node, interface, param, value):
760         """Configure the ethernet parameters of interface.
761
762         :param node: Honeycomb node.
763         :param interface: The name of interface.
764         :param param: Parameter to configure (set, change, remove)
765         :param value: The value of parameter. If None, the parameter will be
766         removed.
767         :type node: dict
768         :type interface: str
769         :type param: str
770         :type value: str
771         :returns: Content of response.
772         :rtype: bytearray
773         :raises HoneycombError: If the parameter is not valid.
774         """
775
776         if param not in InterfaceKeywords.ETH_PARAMS:
777             raise HoneycombError("The parameter {0} is invalid.".format(param))
778         path = ("interfaces", ("interface", "name", interface), "v3po:ethernet",
779                 param)
780         return InterfaceKeywords._set_interface_properties(
781             node, interface, path, value)
782
783     @staticmethod
784     def configure_interface_routing(node, interface, param, value):
785         """Configure the routing parameters of interface.
786
787         :param node: Honeycomb node.
788         :param interface: The name of interface.
789         :param param: Parameter to configure (set, change, remove)
790         :param value: The value of parameter. If None, the parameter will be
791         removed.
792         :type node: dict
793         :type interface: str
794         :type param: str
795         :type value: str
796         :returns: Content of response.
797         :rtype: bytearray
798         :raises HoneycombError: If the parameter is not valid.
799         """
800
801         interface = Topology.convert_interface_reference(
802             node, interface, "name")
803
804         if param not in InterfaceKeywords.ROUTING_PARAMS:
805             raise HoneycombError("The parameter {0} is invalid.".format(param))
806
807         path = ("interfaces", ("interface", "name", interface), "v3po:routing",
808                 param)
809         return InterfaceKeywords._set_interface_properties(
810             node, interface, path, value)
811
812     @staticmethod
813     def honeycomb_create_vxlan_interface(node, interface, **kwargs):
814         """Create a new VxLAN interface.
815
816         :param node: Honeycomb node.
817         :param interface: The name of interface.
818         :param kwargs: Parameters and their values. The accepted parameters are
819         defined in InterfaceKeywords.VXLAN_PARAMS.
820         :type node: dict
821         :type interface: str
822         :type kwargs: dict
823         :returns: Content of response.
824         :rtype: bytearray
825         :raises HoneycombError: If the parameter is not valid.
826         """
827
828         new_vx_lan = {
829             "name": interface,
830             "type": "v3po:vxlan-tunnel",
831             "v3po:vxlan": {}
832         }
833         for param, value in kwargs.items():
834             if param not in InterfaceKeywords.VXLAN_PARAMS:
835                 raise HoneycombError("The parameter {0} is invalid.".
836                                      format(param))
837             new_vx_lan["v3po:vxlan"][param] = value
838
839         path = ("interfaces", "interface")
840         vx_lan_structure = [new_vx_lan, ]
841         return InterfaceKeywords._set_interface_properties(
842             node, interface, path, vx_lan_structure)
843
844     @staticmethod
845     def delete_interface(node, interface):
846         """Delete an interface.
847
848         :param node: Honeycomb node.
849         :param interface: The name of interface.
850         :type node: dict
851         :type interface: str
852         :returns: Content of response.
853         :rtype: bytearray
854         :raises HoneycombError: If it is not possible to get information about
855         interfaces or it is not possible to delete the interface.
856         """
857
858         path = ("interfaces", ("interface", "name", interface))
859
860         status_code, resp = HcUtil.\
861             get_honeycomb_data(node, "config_vpp_interfaces")
862         if status_code != HTTPCodes.OK:
863             raise HoneycombError(
864                 "Not possible to get configuration information about the "
865                 "interfaces. Status code: {0}.".format(status_code))
866
867         new_data = HcUtil.remove_item(resp, path)
868         status_code, resp = HcUtil.\
869             put_honeycomb_data(node, "config_vpp_interfaces", new_data)
870         if status_code != HTTPCodes.OK:
871             raise HoneycombError("Not possible to remove interface {0}. "
872                                  "Status code: {1}.".
873                                  format(interface, status_code))
874         return resp
875
876     @staticmethod
877     def honeycomb_configure_interface_vxlan(node, interface, **kwargs):
878         """Configure VxLAN on the interface.
879
880         The keyword configures VxLAN parameters on the given interface. The type
881         of interface must be set to "v3po:vxlan-tunnel".
882         The new VxLAN parameters overwrite the current configuration. If a
883         parameter in new configuration is missing, it is removed from VxLAN
884         configuration.
885         If the dictionary kwargs is empty, VxLAN configuration is removed.
886
887         :param node: Honeycomb node.
888         :param interface: The name of interface.
889         :param kwargs: Parameters and their values. The accepted parameters are
890         defined in InterfaceKeywords.VXLAN_PARAMS.
891         :type node: dict
892         :type interface: str
893         :type kwargs: dict
894         :returns: Content of response.
895         :rtype: bytearray
896         :raises HoneycombError: If the parameter is not valid.
897         """
898
899         vx_lan_structure = dict()
900         for param, value in kwargs.items():
901             if param not in InterfaceKeywords.VXLAN_PARAMS:
902                 raise HoneycombError("The parameter {0} is invalid.".
903                                      format(param))
904             vx_lan_structure[param] = value
905
906         path = ("interfaces", ("interface", "name", interface), "v3po:vxlan")
907         return InterfaceKeywords._set_interface_properties(
908             node, interface, path, vx_lan_structure)
909
910     @staticmethod
911     def configure_interface_l2(node, interface, param, value):
912         """Configure the L2 parameters of interface.
913
914         :param node: Honeycomb node.
915         :param interface: The name of interface.
916         :param param: Parameter to configure (set, change, remove)
917         :param value: The value of parameter. If None, the parameter will be
918         removed.
919         :type node: dict
920         :type interface: str
921         :type param: str
922         :type value: str
923         :returns: Content of response.
924         :rtype: bytearray
925         :raises HoneycombError: If the parameter is not valid.
926         """
927
928         if param not in InterfaceKeywords.L2_PARAMS:
929             raise HoneycombError("The parameter {0} is invalid.".format(param))
930         path = ("interfaces", ("interface", "name", interface), "v3po:l2",
931                 param)
932         return InterfaceKeywords._set_interface_properties(
933             node, interface, path, value)
934
935     @staticmethod
936     def create_tap_interface(node, interface, **kwargs):
937         """Create a new TAP interface.
938
939         :param node: Honeycomb node.
940         :param interface: The name of interface.
941         :param kwargs: Parameters and their values. The accepted parameters are
942         defined in InterfaceKeywords.TAP_PARAMS.
943         :type node: dict
944         :type interface: str
945         :type kwargs: dict
946         :returns: Content of response.
947         :rtype: bytearray
948         :raises HoneycombError: If the parameter is not valid.
949         """
950
951         new_tap = {
952             "name": interface,
953             "type": "v3po:tap",
954             "v3po:tap": {}
955         }
956         for param, value in kwargs.items():
957             if param not in InterfaceKeywords.TAP_PARAMS:
958                 raise HoneycombError("The parameter {0} is invalid.".
959                                      format(param))
960             new_tap["v3po:tap"][param] = value
961
962         path = ("interfaces", "interface")
963         new_tap_structure = [new_tap, ]
964         return InterfaceKeywords._set_interface_properties(
965             node, interface, path, new_tap_structure)
966
967     @staticmethod
968     def configure_interface_tap(node, interface, **kwargs):
969         """Configure TAP on the interface.
970
971         The keyword configures TAP parameters on the given interface. The type
972         of interface must be set to "v3po:tap".
973         The new TAP parameters overwrite the current configuration. If a
974         parameter in new configuration is missing, it is removed from TAP
975         configuration.
976         If the dictionary kwargs is empty, TAP configuration is removed.
977
978         :param node: Honeycomb node.
979         :param interface: The name of interface.
980         :param kwargs: Parameters and their values. The accepted parameters are
981         defined in InterfaceKeywords.TAP_PARAMS.
982         :type node: dict
983         :type interface: str
984         :type kwargs: dict
985         :returns: Content of response.
986         :rtype: bytearray
987         :raises HoneycombError: If the parameter is not valid.
988         """
989
990         tap_structure = dict()
991         for param, value in kwargs.items():
992             if param not in InterfaceKeywords.TAP_PARAMS:
993                 raise HoneycombError("The parameter {0} is invalid.".
994                                      format(param))
995             tap_structure[param] = value
996
997         path = ("interfaces", ("interface", "name", interface), "v3po:tap")
998         return InterfaceKeywords._set_interface_properties(
999             node, interface, path, tap_structure)
1000
1001     @staticmethod
1002     def configure_interface_vhost_user(node, interface, **kwargs):
1003         """Configure vhost-user on the interface.
1004
1005         The keyword configures vhost-user parameters on the given interface.
1006         The type of interface must be set to "v3po:vhost-user".
1007         The new vhost-user parameters overwrite the current configuration. If a
1008         parameter in new configuration is missing, it is removed from vhost-user
1009         configuration.
1010         If the dictionary kwargs is empty, vhost-user configuration is removed.
1011
1012         :param node: Honeycomb node.
1013         :param interface: The name of interface.
1014         :param kwargs: Parameters and their values. The accepted parameters are
1015         defined in InterfaceKeywords.VHOST_USER_PARAMS.
1016         :type node: dict
1017         :type interface: str
1018         :type kwargs: dict
1019         :returns: Content of response.
1020         :rtype: bytearray
1021         :raises HoneycombError: If the parameter is not valid.
1022         """
1023
1024         vhost_structure = dict()
1025         for param, value in kwargs.items():
1026             if param not in InterfaceKeywords.VHOST_USER_PARAMS:
1027                 raise HoneycombError("The parameter {0} is invalid.".
1028                                      format(param))
1029             vhost_structure[param] = value
1030
1031         path = ("interfaces", ("interface", "name", interface),
1032                 "v3po:vhost-user")
1033         return InterfaceKeywords._set_interface_properties(
1034             node, interface, path, vhost_structure)
1035
1036     @staticmethod
1037     def create_vhost_user_interface(node, interface, **kwargs):
1038         """Create a new vhost-user interface.
1039
1040         :param node: Honeycomb node.
1041         :param interface: The name of interface.
1042         :param kwargs: Parameters and their values. The accepted parameters are
1043         defined in InterfaceKeywords.VHOST_USER_PARAMS.
1044         :type node: dict
1045         :type interface: str
1046         :type kwargs: dict
1047         :returns: Content of response.
1048         :rtype: bytearray
1049         :raises HoneycombError: If the parameter is not valid.
1050         """
1051
1052         new_vhost = {
1053             "name": interface,
1054             "type": "v3po:vhost-user",
1055             "v3po:vhost-user": {}
1056         }
1057         for param, value in kwargs.items():
1058             if param not in InterfaceKeywords.VHOST_USER_PARAMS:
1059                 raise HoneycombError("The parameter {0} is invalid.".
1060                                      format(param))
1061             new_vhost["v3po:vhost-user"][param] = value
1062
1063         path = ("interfaces", "interface")
1064         new_vhost_structure = [new_vhost, ]
1065         return InterfaceKeywords._set_interface_properties(
1066             node, interface, path, new_vhost_structure)
1067
1068     @staticmethod
1069     def honeycomb_create_sub_interface(node, super_interface, match, tags=None,
1070                                        **kwargs):
1071         """Create a new sub-interface.
1072
1073         :param node: Honeycomb node.
1074         :param super_interface: Super interface.
1075         :param match: Match type. The valid values are defined in
1076         InterfaceKeywords.SUB_IF_MATCH.
1077         :param tags: List of tags.
1078         :param kwargs: Parameters and their values. The accepted parameters are
1079         defined in InterfaceKeywords.SUB_IF_PARAMS.
1080         :type node: dict
1081         :type super_interface: str
1082         :type match: str
1083         :type tags: list
1084         :type kwargs: dict
1085         :returns: Content of response.
1086         :rtype: bytearray
1087         :raises HoneycombError: If the parameter is not valid.
1088         :raises KeyError: If the parameter 'match' is invalid.
1089         """
1090
1091         super_interface = Topology.convert_interface_reference(
1092             node, super_interface, "name")
1093
1094         match_type = {
1095             "default":
1096                 {"default": {}},
1097             "untagged":
1098                 {"untagged": {}},
1099             "vlan-tagged":
1100                 {"vlan-tagged": {"match-exact-tags": "false"}},
1101             "vlan-tagged-exact-match":
1102                 {"vlan-tagged": {"match-exact-tags": "true"}}
1103         }
1104
1105         new_sub_interface = {
1106             "tags": {
1107                 "tag": []
1108             },
1109         }
1110
1111         for param, value in kwargs.items():
1112             if param in InterfaceKeywords.SUB_IF_PARAMS:
1113                 new_sub_interface[param] = value
1114             else:
1115                 raise HoneycombError("The parameter {0} is invalid.".
1116                                      format(param))
1117         try:
1118             new_sub_interface["match"] = match_type[match]
1119         except KeyError:
1120             raise HoneycombError("The value '{0}' of parameter 'match' is "
1121                                  "invalid.".format(match))
1122
1123         if tags:
1124             new_sub_interface["tags"]["tag"].extend(tags)
1125
1126         path = ("interfaces",
1127                 ("interface", "name", super_interface),
1128                 "vpp-vlan:sub-interfaces",
1129                 "sub-interface")
1130         new_sub_interface_structure = [new_sub_interface, ]
1131         return InterfaceKeywords._set_interface_properties(
1132             node, super_interface, path, new_sub_interface_structure)
1133
1134     @staticmethod
1135     def get_sub_interface_oper_data(node, super_interface, identifier):
1136         """Retrieves sub-interface operational data using Honeycomb API.
1137
1138         :param node: Honeycomb node.
1139         :param super_interface: Super interface.
1140         :param identifier: The ID of sub-interface.
1141         :type node: dict
1142         :type super_interface: str
1143         :type identifier: int
1144         :returns: Sub-interface operational data.
1145         :rtype: dict
1146         :raises HoneycombError: If there is no sub-interface with the given ID.
1147         """
1148
1149         if_data = InterfaceKeywords.get_interface_oper_data(node,
1150                                                             super_interface)
1151         for sub_if in if_data["vpp-vlan:sub-interfaces"]["sub-interface"]:
1152             if str(sub_if["identifier"]) == str(identifier):
1153                 return sub_if
1154
1155         raise HoneycombError("The interface {0} does not have sub-interface "
1156                              "with ID {1}".format(super_interface, identifier))
1157
1158     @staticmethod
1159     def remove_all_sub_interfaces(node, super_interface):
1160         """Remove all sub-interfaces from the given interface.
1161
1162         :param node: Honeycomb node.
1163         :param super_interface: Super interface.
1164         :type node: dict
1165         :type super_interface: str
1166         :returns: Content of response.
1167         :rtype: bytearray
1168         """
1169
1170         path = ("interfaces",
1171                 ("interface", "name", super_interface),
1172                 "vpp-vlan:sub-interfaces")
1173
1174         return InterfaceKeywords._set_interface_properties(
1175             node, super_interface, path, {})
1176
1177     @staticmethod
1178     def set_sub_interface_state(node, super_interface, identifier, state):
1179         """Set the administrative state of sub-interface.
1180
1181         :param node: Honeycomb node.
1182         :param super_interface: Super interface.
1183         :param identifier: The ID of sub-interface.
1184         :param state: Required sub-interface state - up or down.
1185         :type node: dict
1186         :type super_interface: str
1187         :type identifier: int
1188         :type state: str
1189         :returns: Content of response.
1190         :rtype: bytearray
1191         """
1192
1193         super_interface = Topology.convert_interface_reference(
1194             node, super_interface, "name")
1195
1196         intf_state = {"up": "true",
1197                       "down": "false"}
1198
1199         path = ("interfaces",
1200                 ("interface", "name", super_interface),
1201                 "vpp-vlan:sub-interfaces",
1202                 ("sub-interface", "identifier", int(identifier)),
1203                 "enabled")
1204
1205         return InterfaceKeywords._set_interface_properties(
1206             node, super_interface, path, intf_state[state])
1207
1208     @staticmethod
1209     def add_bridge_domain_to_sub_interface(node, super_interface, identifier,
1210                                            config):
1211         """Add a sub-interface to a bridge domain and set its parameters.
1212
1213         :param node: Honeycomb node.
1214         :param super_interface: Super interface.
1215         :param identifier: The ID of sub-interface.
1216         :param config: Bridge domain configuration.
1217         :type node: dict
1218         :type super_interface: str
1219         :type identifier: int
1220         :type config: dict
1221         :returns: Content of response.
1222         :rtype: bytearray
1223         """
1224
1225         path = ("interfaces",
1226                 ("interface", "name", super_interface),
1227                 "vpp-vlan:sub-interfaces",
1228                 ("sub-interface", "identifier", int(identifier)),
1229                 "l2")
1230
1231         return InterfaceKeywords._set_interface_properties(
1232             node, super_interface, path, config)
1233
1234     @staticmethod
1235     def get_bd_data_from_sub_interface(node, super_interface, identifier):
1236         """Get the operational data about the bridge domain from sub-interface.
1237
1238         :param node: Honeycomb node.
1239         :param super_interface: Super interface.
1240         :param identifier: The ID of sub-interface.
1241         :type node: dict
1242         :type super_interface: str
1243         :type identifier: int
1244         :returns: Operational data about the bridge domain.
1245         :rtype: dict
1246         :raises HoneycombError: If there is no sub-interface with the given ID.
1247         """
1248
1249         try:
1250             bd_data = InterfaceKeywords.get_sub_interface_oper_data(
1251                 node, super_interface, identifier)["l2"]
1252             return bd_data
1253         except KeyError:
1254             raise HoneycombError("The operational data does not contain "
1255                                  "information about a bridge domain.")
1256
1257     @staticmethod
1258     def configure_tag_rewrite(node, super_interface, identifier, config):
1259         """Add / change / disable vlan tag rewrite on a sub-interface.
1260
1261         :param node: Honeycomb node.
1262         :param super_interface: Super interface.
1263         :param identifier: The ID of sub-interface.
1264         :param config: Rewrite tag configuration.
1265         :type node: dict
1266         :type super_interface: str
1267         :type identifier: int
1268         :type config: dict
1269         :returns: Content of response.
1270         :rtype: bytearray
1271         """
1272
1273         path = ("interfaces",
1274                 ("interface", "name", super_interface),
1275                 "vpp-vlan:sub-interfaces",
1276                 ("sub-interface", "identifier", int(identifier)),
1277                 "l2",
1278                 "rewrite")
1279
1280         return InterfaceKeywords._set_interface_properties(
1281             node, super_interface, path, config)
1282
1283     @staticmethod
1284     def get_tag_rewrite_oper_data(node, super_interface, identifier):
1285         """Get the operational data about tag rewrite.
1286
1287         :param node: Honeycomb node.
1288         :param super_interface: Super interface.
1289         :param identifier: The ID of sub-interface.
1290         :type node: dict
1291         :type super_interface: str
1292         :type identifier: int
1293         :returns: Operational data about tag rewrite.
1294         :rtype: dict
1295         :raises HoneycombError: If there is no sub-interface with the given ID.
1296         """
1297
1298         try:
1299             tag_rewrite = InterfaceKeywords.get_sub_interface_oper_data(
1300                 node, super_interface, identifier)["l2"]["rewrite"]
1301             return tag_rewrite
1302         except KeyError:
1303             raise HoneycombError("The operational data does not contain "
1304                                  "information about the tag-rewrite.")
1305
1306     @staticmethod
1307     def add_ip_address_to_sub_interface(node, super_interface, identifier,
1308                                         ip_addr, network, ip_version):
1309         """Add an ipv4 address to the specified sub-interface, with the provided
1310         netmask or network prefix length. Any existing ipv4 addresses on the
1311         sub-interface will be replaced.
1312
1313         :param node: Honeycomb node.
1314         :param super_interface: Super interface.
1315         :param identifier: The ID of sub-interface.
1316         :param ip_addr: IPv4 address to be set.
1317         :param network: Network mask or network prefix length.
1318         :param ip_version: ipv4 or ipv6
1319         :type node: dict
1320         :type super_interface: str
1321         :type identifier: int
1322         :type ip_addr: str
1323         :type network: str or int
1324         :type ip_version: string
1325         :returns: Content of response.
1326         :rtype: bytearray
1327         :raises HoneycombError: If the provided netmask or prefix is not valid.
1328         """
1329
1330         path = ("interfaces",
1331                 ("interface", "name", super_interface),
1332                 "vpp-vlan:sub-interfaces",
1333                 ("sub-interface", "identifier", int(identifier)),
1334                 ip_version.lower())
1335
1336         if isinstance(network, basestring) and ip_version.lower() == "ipv4":
1337             address = {"address": [{"ip": ip_addr, "netmask": network}, ]}
1338
1339         elif isinstance(network, int) and 0 < network < 33:
1340             address = {"address": [{"ip": ip_addr, "prefix-length": network}, ]}
1341
1342         else:
1343             raise HoneycombError("{0} is not a valid netmask or prefix length."
1344                                  .format(network))
1345
1346         return InterfaceKeywords._set_interface_properties(
1347             node, super_interface, path, address)
1348
1349     @staticmethod
1350     def remove_all_ip_addresses_from_sub_interface(node, super_interface,
1351                                                    identifier, ip_version):
1352         """Remove all ipv4 addresses from the specified sub-interface.
1353
1354         :param node: Honeycomb node.
1355         :param super_interface: Super interface.
1356         :param identifier: The ID of sub-interface.
1357         :param ip_version: ipv4 or ipv6
1358         :type node: dict
1359         :type super_interface: str
1360         :type identifier: int
1361         :type ip_version: string
1362         :returns: Content of response.
1363         :rtype: bytearray
1364         """
1365
1366         path = ("interfaces",
1367                 ("interface", "name", super_interface),
1368                 "vpp-vlan:sub-interfaces",
1369                 ("sub-interface", "identifier", int(identifier)),
1370                 str(ip_version), "address")
1371
1372         return InterfaceKeywords._set_interface_properties(
1373             node, super_interface, path, None)
1374
1375     @staticmethod
1376     def compare_data_structures(data, ref, _path=''):
1377         """Checks if data obtained from UUT is as expected. If it is not,
1378         proceeds down the list/dictionary tree and finds the point of mismatch.
1379
1380         :param data: Data to be checked.
1381         :param ref: Referential data used for comparison.
1382         :param _path: Used in recursive calls, stores the path taken down
1383         the JSON tree.
1384         :type data: dict
1385         :type ref: dict
1386         :type _path: str
1387
1388         :raises HoneycombError: If the data structures do not match in some way,
1389         or if they are not in deserialized JSON format.
1390         """
1391
1392         if data == ref:
1393             return True
1394
1395         elif isinstance(data, dict) and isinstance(ref, dict):
1396             for key in ref:
1397                 if key not in data:
1398                     raise HoneycombError(
1399                         "Key {key} is not present in path {path}. Keys in path:"
1400                         "{data_keys}".format(
1401                             key=key,
1402                             path=_path,
1403                             data_keys=data.keys()))
1404
1405                 if data[key] != ref[key]:
1406                     if isinstance(data[key], list) \
1407                             or isinstance(data[key], dict):
1408                         InterfaceKeywords.compare_data_structures(
1409                             data[key], ref[key],
1410                             _path + '[{0}]'.format(key))
1411                     else:
1412                         raise HoneycombError(
1413                             "Data mismatch, key {key} in path {path} has value"
1414                             " {data}, but should be {ref}".format(
1415                                 key=key,
1416                                 path=_path,
1417                                 data=data[key],
1418                                 ref=ref[key]))
1419
1420         elif isinstance(data, list) and isinstance(ref, list):
1421             for item in ref:
1422                 if item not in data:
1423                     if isinstance(item, dict):
1424                         InterfaceKeywords.compare_data_structures(
1425                             data[0], item,
1426                             _path + '[{0}]'.format(ref.index(item)))
1427                     else:
1428                         raise HoneycombError(
1429                             "Data mismatch, list item {index} in path {path}"
1430                             " has value {data}, but should be {ref}".format(
1431                                 index=ref.index(item),
1432                                 path=_path,
1433                                 data=data[0],
1434                                 ref=item))
1435
1436         else:
1437             raise HoneycombError(
1438                 "Unexpected data type {data_type} in path {path}, reference"
1439                 " type is {ref_type}. Must be list or dictionary.".format(
1440                     data_type=type(data),
1441                     ref_type=type(ref),
1442                     path=_path))
1443
1444     @staticmethod
1445     def compare_interface_lists(list1, list2):
1446         """Compare provided lists of interfaces by name.
1447
1448         :param list1: List of interfaces.
1449         :param list2: List of interfaces.
1450         :type list1: list
1451         :type list2: list
1452         :raises HoneycombError: If an interface exists in only one of the lists.
1453         """
1454
1455         ignore = ["vx_tunnel0", "vxlan_gpe_tunnel0"]
1456         # these have no equivalent in config data and no effect on VPP
1457
1458         names1 = [x['name'] for x in list1]
1459         names2 = [x['name'] for x in list2]
1460
1461         for name in names1:
1462             if name not in names2 and name not in ignore:
1463                 raise HoneycombError("Interface {0} not present in list {1}"
1464                                      .format(name, list2))
1465         for name in names2:
1466             if name not in names1 and name not in ignore:
1467                 raise HoneycombError("Interface {0} not present in list {1}"
1468                                      .format(name, list1))
1469
1470     @staticmethod
1471     def create_vxlan_gpe_interface(node, interface, **kwargs):
1472         """Create a new VxLAN GPE interface.
1473
1474         :param node: Honeycomb node.
1475         :param interface: The name of interface to be created.
1476         :param kwargs: Parameters and their values. The accepted parameters are
1477         defined in InterfaceKeywords.VXLAN_GPE_PARAMS.
1478         :type node: dict
1479         :type interface: str
1480         :type kwargs: dict
1481         :returns: Content of response.
1482         :rtype: bytearray
1483         :raises HoneycombError: If a parameter in kwargs is not valid.
1484         """
1485
1486         new_vxlan_gpe = {
1487             "name": interface,
1488             "type": "v3po:vxlan-gpe-tunnel",
1489             "v3po:vxlan-gpe": {}
1490         }
1491         for param, value in kwargs.items():
1492             if param in InterfaceKeywords.INTF_PARAMS:
1493                 new_vxlan_gpe[param] = value
1494             elif param in InterfaceKeywords.VXLAN_GPE_PARAMS:
1495                 new_vxlan_gpe["v3po:vxlan-gpe"][param] = value
1496             else:
1497                 raise HoneycombError("The parameter {0} is invalid.".
1498                                      format(param))
1499         path = ("interfaces", "interface")
1500         vxlan_gpe_structure = [new_vxlan_gpe, ]
1501         return InterfaceKeywords._set_interface_properties(
1502             node, interface, path, vxlan_gpe_structure)
1503
1504     @staticmethod
1505     def enable_acl_on_interface(node, interface, table_name):
1506         """Enable ACL on the given interface.
1507
1508         :param node: Honeycomb node.
1509         :param interface: The interface where the ACL will be enabled.
1510         :param table_name: Name of the classify table.
1511         :type node: dict
1512         :type interface: str
1513         :type table_name: str
1514         :returns: Content of response.
1515         :rtype: bytearray
1516         :raises HoneycombError: If the configuration of interface is not
1517         successful.
1518         """
1519
1520         interface = interface.replace("/", "%2F")
1521
1522         data = {
1523             "vpp-interface-acl:acl": {
1524                 "ingress": {
1525                     "ip4-acl": {
1526                         "classify-table": table_name
1527                     },
1528                     "l2-acl": {
1529                         "classify-table": table_name
1530                     }
1531                 }
1532             }
1533         }
1534
1535         path = "/interface/" + interface + "/vpp-interface-acl:acl"
1536         status_code, resp = HcUtil.\
1537             put_honeycomb_data(node, "config_vpp_interfaces", data, path,
1538                                data_representation=DataRepresentation.JSON)
1539         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
1540             raise HoneycombError(
1541                 "The configuration of interface '{0}' was not successful. "
1542                 "Status code: {1}.".format(interface, status_code))
1543         return resp
1544
1545     @staticmethod
1546     def enable_policer_on_interface(node, interface, table_name):
1547         """Enable Policer on the given interface.
1548
1549         :param node: Honeycomb node.
1550         :param interface: The interface where policer will be enabled.
1551         :param table_name: Name of the classify table.
1552         :type node: dict
1553         :type interface: str
1554         :type table_name: str
1555         :returns: Content of response.
1556         :rtype: bytearray
1557         :raises HoneycombError: If the configuration of interface is not
1558         successful.
1559         """
1560         interface = Topology.convert_interface_reference(
1561             node, interface, "name")
1562         interface = interface.replace("/", "%2F")
1563
1564         data = {
1565             "interface-policer:policer": {
1566                 "ip4-table": table_name
1567                 }
1568             }
1569
1570         path = "/interface/" + interface + "/interface-policer:policer"
1571         status_code, resp = HcUtil.\
1572             put_honeycomb_data(node, "config_vpp_interfaces", data, path,
1573                                data_representation=DataRepresentation.JSON)
1574         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
1575             raise HoneycombError(
1576                 "The configuration of interface '{0}' was not successful. "
1577                 "Status code: {1}.".format(interface, status_code))
1578         return resp
1579
1580     @staticmethod
1581     def disable_policer_on_interface(node, interface):
1582         """Disable Policer on the given interface.
1583
1584         :param node: Honeycomb node.
1585         :param interface: The interface where policer will be disabled.
1586         :type node: dict
1587         :type interface: str
1588         :returns: Content of response.
1589         :rtype: bytearray
1590         :raises HoneycombError: If the configuration of interface is not
1591         successful.
1592         """
1593         interface = Topology.convert_interface_reference(
1594             node, interface, "name")
1595         interface = interface.replace("/", "%2F")
1596
1597         path = "/interface/" + interface + "/interface-policer:policer"
1598         status_code, resp = HcUtil.\
1599             delete_honeycomb_data(node, "config_vpp_interfaces", path)
1600         if status_code != HTTPCodes.OK:
1601             raise HoneycombError(
1602                 "The configuration of interface '{0}' was not successful. "
1603                 "Status code: {1}.".format(interface, status_code))
1604         return resp
1605
1606     @staticmethod
1607     def disable_acl_on_interface(node, interface):
1608         """Disable ACL on the given interface.
1609
1610         :param node: Honeycomb node.
1611         :param interface: The interface where the ACL will be disabled.
1612         :type node: dict
1613         :type interface: str
1614         :returns: Content of response.
1615         :rtype: bytearray
1616         :raises HoneycombError: If the configuration of interface is not
1617         successful.
1618         """
1619
1620         interface = interface.replace("/", "%2F")
1621
1622         path = "/interface/" + interface + "/vpp-interface-acl:acl"
1623
1624         status_code, resp = HcUtil.\
1625             delete_honeycomb_data(node, "config_vpp_interfaces", path)
1626
1627         if status_code != HTTPCodes.OK:
1628             raise HoneycombError(
1629                 "The configuration of interface '{0}' was not successful. "
1630                 "Status code: {1}.".format(interface, status_code))
1631         return resp
1632
1633     @staticmethod
1634     def create_pbb_sub_interface(node, intf, params):
1635         """Creates a PBB sub-interface on the given interface and sets its
1636         parameters.
1637
1638         :param node: Honeycomb node.
1639         :param intf: The interface where PBB sub-interface will be configured.
1640         :param params: Configuration parameters of the sub-interface to be
1641         created.
1642         :type node: dict
1643         :type intf: str
1644         :type params: dict
1645         :returns: Content of response.
1646         :rtype: bytearray
1647         :raises HoneycombError: If the configuration of sub-interface is not
1648         successful.
1649         """
1650
1651         interface = intf.replace("/", "%2F")
1652         path = "/interface/{0}/pbb-rewrite".format(interface)
1653         status_code, resp = HcUtil. \
1654             put_honeycomb_data(node, "config_vpp_interfaces", params, path,
1655                                data_representation=DataRepresentation.JSON)
1656         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
1657             raise HoneycombError(
1658                 "The configuration of PBB sub-interface '{0}' was not "
1659                 "successful. Status code: {1}.".format(intf, status_code))
1660         return resp
1661
1662     @staticmethod
1663     def delete_pbb_sub_interface(node, intf):
1664         """Deletes the given PBB sub-interface.
1665
1666         :param node: Honeycomb node.
1667         :param intf: The interface where PBB sub-interface will be deleted.
1668         :type node: dict
1669         :type intf: str
1670         :returns: Content of response.
1671         :rtype: bytearray
1672         :raises HoneycombError: If the removal of sub-interface is not
1673         successful.
1674         """
1675
1676         interface = intf.replace("/", "%2F")
1677         path = "/interface/{0}/pbb-rewrite".format(interface)
1678
1679         status_code, resp = HcUtil. \
1680             delete_honeycomb_data(node, "config_vpp_interfaces", path)
1681         if status_code != HTTPCodes.OK:
1682             raise HoneycombError(
1683                 "The removal of pbb sub-interface '{0}' was not successful. "
1684                 "Status code: {1}.".format(intf, status_code))
1685         return resp
1686
1687     @staticmethod
1688     def get_pbb_sub_interface_oper_data(node, intf, sub_if_id):
1689         """Retrieves PBB sub-interface operational data from Honeycomb.
1690
1691         :param node: Honeycomb node.
1692         :param intf: The interface where PBB sub-interface is located.
1693         :param sub_if_id: ID of the PBB sub-interface.
1694         :type node: dict
1695         :type intf: str
1696         :type sub_if_id: str or int
1697         :returns: PBB sub-interface operational data.
1698         :rtype: dict
1699         :raises HoneycombError: If the removal of sub-interface is not
1700         successful.
1701         """
1702
1703         raise NotImplementedError
1704
1705     @staticmethod
1706     def check_disabled_interface(node, interface):
1707         """Retrieves list of disabled interface indices from Honeycomb,
1708         and matches with the provided interface by index.
1709
1710         :param node: Honeycomb node.
1711         :param interface: Index number of an interface on the node.
1712         :type node: dict
1713         :type interface: int
1714         :returns: True if the interface exists in disabled interfaces.
1715         :rtype: bool
1716         :raises HoneycombError: If the interface is not present
1717          in retrieved list of disabled interfaces.
1718          """
1719         data = InterfaceKeywords.get_disabled_interfaces_oper_data(node)
1720         # decrement by one = conversion from HC if-index to VPP sw_if_index
1721         interface -= 1
1722
1723         for item in data:
1724             if item["index"] == interface:
1725                 return True
1726         raise HoneycombError("Interface index {0} not present in list"
1727                              " of disabled interfaces.".format(interface))
1728
1729     @staticmethod
1730     def configure_interface_span(node, dst_interface, src_interfaces=None):
1731         """Configure SPAN port mirroring on the specified interfaces. If no
1732          source interface is provided, SPAN will be disabled.
1733
1734         :param node: Honeycomb node.
1735         :param dst_interface: Interface to mirror packets to.
1736         :param src_interfaces: List of interfaces to mirror packets from.
1737         :type node: dict
1738         :type dst_interface: str or int
1739         :type src_interfaces: list of dict
1740         :returns: Content of response.
1741         :rtype: bytearray
1742         :raises HoneycombError: If SPAN could not be configured.
1743         """
1744
1745         interface = Topology.convert_interface_reference(
1746             node, dst_interface, "name")
1747         interface = interface.replace("/", "%2F")
1748         path = "/interface/" + interface + "/span"
1749
1750         if not src_interfaces:
1751             status_code, _ = HcUtil.delete_honeycomb_data(
1752                 node, "config_vpp_interfaces", path)
1753         else:
1754             for src_interface in src_interfaces:
1755                 src_interface["iface-ref"] = Topology.\
1756                     convert_interface_reference(
1757                         node, src_interface["iface-ref"], "name")
1758             data = {
1759                 "span": {
1760                     "mirrored-interfaces": {
1761                         "mirrored-interface": src_interfaces
1762                     }
1763                 }
1764             }
1765
1766             status_code, _ = HcUtil.put_honeycomb_data(
1767                 node, "config_vpp_interfaces", data, path)
1768
1769         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
1770             raise HoneycombError(
1771                 "Configuring SPAN failed. Status code:{0}".format(status_code))
1772
1773     @staticmethod
1774     def configure_sub_interface_span(node, super_interface, dst_interface_index,
1775                                      src_interfaces=None):
1776         """Configure SPAN port mirroring on the specified sub-interface. If no
1777          source interface is provided, SPAN will be disabled.
1778
1779         Note: Does not support source sub-interfaces, only destination.
1780
1781         :param node: Honeycomb node.
1782         :param super_interface: Name, link name or sw_if_index
1783         of the destination interface's super-interface.
1784         :param dst_interface_index: Index of sub-interface to mirror packets to.
1785         :param src_interfaces: List of interfaces to mirror packets from.
1786         :type node: dict
1787         :type super_interface: str or int
1788         :type dst_interface_index: int
1789         :type src_interfaces: list of dict
1790         :returns: Content of response.
1791         :rtype: bytearray
1792         :raises HoneycombError: If SPAN could not be configured.
1793         """
1794
1795         super_interface = Topology.convert_interface_reference(
1796             node, super_interface, "name")
1797         super_interface = super_interface.replace("/", "%2F")
1798
1799         path = "/interface/{0}/vpp-vlan:sub-interfaces/sub-interface/{1}/span"\
1800             .format(super_interface, dst_interface_index)
1801
1802         if not src_interfaces:
1803             status_code, _ = HcUtil.delete_honeycomb_data(
1804                 node, "config_vpp_interfaces", path)
1805         else:
1806             for src_interface in src_interfaces:
1807                 src_interface["iface-ref"] = Topology. \
1808                     convert_interface_reference(
1809                         node, src_interface["iface-ref"], "name")
1810             data = {
1811                 "span": {
1812                     "mirrored-interfaces": {
1813                         "mirrored-interface": src_interfaces
1814                     }
1815                 }
1816             }
1817
1818             status_code, _ = HcUtil.put_honeycomb_data(
1819                 node, "config_vpp_interfaces", data, path)
1820
1821         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
1822             raise HoneycombError(
1823                 "Configuring SPAN failed. Status code:{0}".format(status_code))
1824
1825     @staticmethod
1826     def add_interface_local0_to_topology(node):
1827         """Use Topology methods to add interface "local0" to working topology,
1828         if not already present.
1829
1830         :param node: DUT node.
1831         :type node: dict
1832         """
1833
1834         if Topology.get_interface_by_sw_index(node, 0) is None:
1835             local0_key = Topology.add_new_port(node, "localzero")
1836             Topology.update_interface_sw_if_index(node, local0_key, 0)
1837             Topology.update_interface_name(node, local0_key, "local0")
1838
1839     @staticmethod
1840     def configure_interface_unnumbered(node, interface, interface_src=None):
1841         """Configure the specified interface as unnumbered. The interface
1842         borrows IP address from the specified source interface. If not source
1843         interface is provided, unnumbered configuration will be removed.
1844
1845         :param node: Honeycomb node.
1846         :param interface: Name, link name or sw_if_index of an interface.
1847         :param interface_src: Name of source interface.
1848         :type node: dict
1849         :type interface: str or int
1850         :type interface_src: str
1851         :raises HoneycombError: If the configuration fails.
1852         """
1853
1854         interface = InterfaceKeywords.handle_interface_reference(
1855             node, interface)
1856
1857         path = "/interface/{0}/unnumbered-interfaces:unnumbered"\
1858             .format(interface)
1859
1860         if interface_src:
1861             data = {
1862                 "unnumbered": {
1863                     "use": interface_src
1864                 }
1865             }
1866             status_code, _ = HcUtil.put_honeycomb_data(
1867                 node, "config_vpp_interfaces", data, path)
1868         else:
1869             status_code, _ = HcUtil.delete_honeycomb_data(
1870                 node, "config_vpp_interfaces", path)
1871
1872         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
1873             raise HoneycombError(
1874                 "Configuring unnumbered interface failed. "
1875                 "Status code:{0}".format(status_code))
1876
1877     @staticmethod
1878     def handle_interface_reference(node, interface):
1879         """Convert any interface reference to interface name used by Honeycomb.
1880
1881         :param node: Honeycomb node.
1882         :param interface: Name, link name or sw_if_index of an interface,
1883         name of a custom interface or name of a sub-interface.
1884         :type node: Honeycomb node.
1885         :type interface: str or int
1886         :returns: Name of interface that can be used in Honeycomb requests.
1887         :rtype: str
1888         """
1889
1890         try:
1891             interface = Topology.convert_interface_reference(
1892                 node, interface, "name")
1893             interface = interface.replace("/", "%2F")
1894         except RuntimeError:
1895             # interface is not in topology
1896             if "." in interface:
1897                 # Assume it's the name of a sub-interface
1898                 interface, index = interface.split(".")
1899                 interface = interface.replace("/", "%2F")
1900                 interface = "{0}/vpp-vlan:sub-interfaces/sub-interface/{1}".\
1901                     format(interface, index)
1902             else:
1903                 # Assume it's the name of a custom interface (pbb, vxlan, etc.)
1904                 interface = interface.replace("/", "%2F")
1905
1906         return interface