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