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