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