Honeycomb API keywords - interface, bridge domain 13/913/6
authorTibor Frank <tifrank@cisco.com>
Thu, 28 Apr 2016 13:44:25 +0000 (15:44 +0200)
committerMatej Klotton <mklotton@cisco.com>
Fri, 6 May 2016 14:57:37 +0000 (14:57 +0000)
- add keywords to manipulate data  using Honeycomb REST API to configure
  interfaces and bridge domains
- remove "add_vpp_to_honeycomb_network_topology" method from
  HoneycombSetup.py
- remove "parse_json_response" from HoneycombUtil.py
- add methods to manipulate data from Honeycomb REST API

Change-Id: I5e6f87097fe9bfccffa3d4aae21f63281353cf29
Signed-off-by: Tibor Frank <tifrank@cisco.com>
resources/libraries/python/HcAPIKwBridgeDomain.py [new file with mode: 0644]
resources/libraries/python/HcAPIKwInterfaces.py [new file with mode: 0644]
resources/libraries/python/HoneycombAPIKeywords.py [deleted file]
resources/libraries/python/HoneycombSetup.py
resources/libraries/python/HoneycombUtil.py

diff --git a/resources/libraries/python/HcAPIKwBridgeDomain.py b/resources/libraries/python/HcAPIKwBridgeDomain.py
new file mode 100644 (file)
index 0000000..2f7c149
--- /dev/null
@@ -0,0 +1,329 @@
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Keywords to manipulate bridge domain configuration using Honeycomb REST API.
+
+The keywords make possible to put and get configuration data and to get
+operational data.
+"""
+
+
+from resources.libraries.python.HTTPRequest import HTTPCodes
+from resources.libraries.python.HoneycombSetup import HoneycombError
+from resources.libraries.python.HoneycombUtil import HoneycombUtil as HcUtil
+from resources.libraries.python.HoneycombUtil import DataRepresentation
+
+
+class BridgeDomainKeywords(object):
+    """Keywords to manipulate bridge domain configuration.
+
+    Implements keywords which get configuration and operational data about
+    bridge domains and put the bridge domains' parameters using Honeycomb REST
+    API.
+    """
+
+    PARAMS = ("flood", "forward", "learn", "unknown-unicast-flood",
+              "arp-termination")
+
+    def __init__(self):
+        pass
+
+    @staticmethod
+    def _configure_bd(node, bd_name, data,
+                      data_representation=DataRepresentation.JSON):
+        """Send bridge domain configuration data and check the response.
+
+        :param node: Honeycomb node.
+        :param bd_name: The name of bridge domain.
+        :param data: Configuration data to be sent in PUT request.
+        :param data_representation: How the data is represented.
+        :type node: dict
+        :type bd_name: str
+        :type data: dict
+        :type data_representation: DataRepresentation
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If the status code in response on PUT is not
+        200 = OK.
+        """
+
+        status_code, resp = HcUtil.\
+            put_honeycomb_data(node, "config_bridge_domain", data,
+                               data_representation=data_representation)
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError(
+                "The configuration of bridge domain '{0}' was not successful. "
+                "Status code: {1}.".format(bd_name, status_code))
+        return resp
+
+    @staticmethod
+    def _set_bd_properties(node, bd_name, path, new_value=None):
+        """Set bridge domain properties.
+
+        This method reads bridge domain configuration data, creates, changes or
+        removes the requested data and puts it back to Honeycomb.
+
+        :param node: Honeycomb node.
+        :param bd_name: The name of bridge domain.
+        :param path:  Path to data we want to change, create or remove.
+        :param new_value: The new value to be set. If None, the item will be
+        removed.
+        :type node: dict
+        :type bd_name: str
+        :type path: tuple
+        :type new_value: str, dict or list
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If it is not possible to get or set the data.
+        """
+
+        status_code, resp = HcUtil.\
+            get_honeycomb_data(node, "config_bridge_domain")
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError(
+                "Not possible to get configuration information about the "
+                "bridge domains. Status code: {0}.".format(status_code))
+
+        if new_value:
+            new_data = HcUtil.set_item_value(resp, path, new_value)
+        else:
+            new_data = HcUtil.remove_item(resp, path)
+        return BridgeDomainKeywords._configure_bd(node, bd_name, new_data)
+
+    @staticmethod
+    def _create_bd_structure(bd_name, **kwargs):
+        """Create the bridge domain data structure as it is expected by
+        Honeycomb REST API.
+
+        :param bd_name: Bridge domain name.
+        :param kwargs: Parameters and their values. The accepted parameters are
+        defined in BridgeDomainKeywords.PARAMS.
+        :type bd_name: str
+        :type kwargs: dict
+        :return: Bridge domain data structure.
+        :rtype: dict
+        """
+
+        bd_structure = {"name": bd_name}
+
+        for param, value in kwargs.items():
+            if param not in BridgeDomainKeywords.PARAMS:
+                raise HoneycombError("The parameter {0} is invalid.".
+                                     format(param))
+            bd_structure[param] = str(value)
+
+        return bd_structure
+
+    @staticmethod
+    def get_all_bds_cfg_data(node):
+        """Get configuration data about all bridge domains from Honeycomb.
+
+        :param node: Honeycomb node.
+        :type node: dict
+        :return: Configuration data about all bridge domains from Honeycomb.
+        :rtype: list
+        :raises HoneycombError: If it is not possible to get configuration data.
+        """
+
+        status_code, resp = HcUtil.\
+            get_honeycomb_data(node, "config_bridge_domain")
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError(
+                "Not possible to get configuration information about the "
+                "bridge domains. Status code: {0}.".format(status_code))
+        try:
+            return resp["bridge-domains"]["bridge-domain"]
+
+        except (KeyError, TypeError):
+            return []
+
+    @staticmethod
+    def get_bd_cfg_data(node, bd_name):
+        """Get configuration data about the given bridge domain from Honeycomb.
+
+        :param node: Honeycomb node.
+        :param bd_name: The name of bridge domain.
+        :type node: dict
+        :type bd_name: str
+        :return: Configuration data about the given bridge domain from
+        Honeycomb.
+        :rtype: dict
+        """
+
+        intfs = BridgeDomainKeywords.get_all_bds_cfg_data(node)
+        for intf in intfs:
+            if intf["name"] == bd_name:
+                return intf
+        return {}
+
+    @staticmethod
+    def get_all_bds_oper_data(node):
+        """Get operational data about all bridge domains from Honeycomb.
+
+        :param node: Honeycomb node.
+        :type node: dict
+        :return: Operational data about all bridge domains from Honeycomb.
+        :rtype: list
+        :raises HoneycombError: If it is not possible to get operational data.
+        """
+
+        status_code, resp = HcUtil.\
+            get_honeycomb_data(node, "oper_bridge_domains")
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError(
+                "Not possible to get operational information about the "
+                "bridge domains. Status code: {0}.".format(status_code))
+        try:
+            return resp["bridge-domains"]["bridge-domain"]
+
+        except (KeyError, TypeError):
+            return []
+
+    @staticmethod
+    def get_bd_oper_data(node, bd_name):
+        """Get operational data about the given bridge domain from Honeycomb.
+
+        :param node: Honeycomb node.
+        :param bd_name: The name of bridge domain.
+        :type node: dict
+        :type bd_name: str
+        :return: Operational data about the given bridge domain from Honeycomb.
+        :rtype: dict
+        """
+
+        intfs = BridgeDomainKeywords.get_all_bds_oper_data(node)
+        for intf in intfs:
+            if intf["name"] == bd_name:
+                return intf
+        return {}
+
+    @staticmethod
+    def add_first_bd(node, bd_name, **kwargs):
+        """Add the first bridge domain.
+
+        If there are any other bridge domains configured, they will be removed.
+
+        :param node: Honeycomb node.
+        :param bd_name: Bridge domain name.
+        :param kwargs: Parameters and their values. The accepted parameters are
+        defined in BridgeDomainKeywords.PARAMS
+        :type node: dict
+        :type bd_name: str
+        :type kwargs: dict
+        :return: Bridge domain data structure.
+        :rtype: dict
+        """
+
+        path = ("bridge-domains", )
+        new_bd = BridgeDomainKeywords._create_bd_structure(bd_name, **kwargs)
+        bridge_domain = {"bridge-domain": [new_bd, ]}
+        return BridgeDomainKeywords._set_bd_properties(node, bd_name, path,
+                                                       bridge_domain)
+
+    @staticmethod
+    def add_bd(node, bd_name, **kwargs):
+        """Add a bridge domain.
+
+        :param node: Honeycomb node.
+        :param bd_name: Bridge domain name.
+        :param kwargs: Parameters and their values. The accepted parameters are
+        defined in BridgeDomainKeywords.PARAMS
+        :type node: dict
+        :type bd_name: str
+        :type kwargs: dict
+        :return: Bridge domain data structure.
+        :rtype: dict
+        """
+
+        path = ("bridge-domains", "bridge-domain")
+        new_bd = BridgeDomainKeywords._create_bd_structure(bd_name, **kwargs)
+        bridge_domain = [new_bd, ]
+        return BridgeDomainKeywords._set_bd_properties(node, bd_name, path,
+                                                       bridge_domain)
+
+    @staticmethod
+    def remove_all_bds(node):
+        """Remove all bridge domains.
+
+        :param node: Honeycomb node.
+        :type node: dict
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If it is not possible to remove all bridge
+        domains.
+        """
+
+        data = {"bridge-domains": {"bridge-domain": []}}
+        status_code, resp = HcUtil.\
+            put_honeycomb_data(node, "config_bridge_domain", data)
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError("Not possible to remove all bridge domains. "
+                                 "Status code: {0}.".format(status_code))
+        return resp
+
+    @staticmethod
+    def remove_bridge_domain(node, bd_name):
+        """Remove a bridge domain.
+
+        :param node:  Honeycomb node.
+        :param bd_name: The name of bridge domain to be removed.
+        :type node: dict
+        :type bd_name: str
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError:If it is not possible to remove the bridge
+        domain.
+        """
+
+        path = ("bridge-domains", ("bridge-domain", "name", bd_name))
+
+        status_code, resp = HcUtil.\
+            get_honeycomb_data(node, "config_bridge_domain")
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError(
+                "Not possible to get configuration information about the "
+                "bridge domains. Status code: {0}.".format(status_code))
+
+        new_data = HcUtil.remove_item(resp, path)
+        status_code, resp = HcUtil.\
+            put_honeycomb_data(node, "config_bridge_domain", new_data)
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError("Not possible to remove bridge domain {0}. "
+                                 "Status code: {1}.".
+                                 format(bd_name, status_code))
+        return resp
+
+    @staticmethod
+    def configure_bridge_domain(node, bd_name, param, value):
+        """Configure a bridge domain.
+
+        :param node: Honeycomb node.
+        :param bd_name: Bridge domain name.
+        :param param: Parameter to set, change or remove. The accepted
+        parameters are defined in BridgeDomainKeywords.PARAMS
+        :param value: The new value to be set, change or remove. If None, the
+        item will be removed.
+        :type node: dict
+        :type bd_name: str
+        :type param: str
+        :type value: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        if param not in BridgeDomainKeywords.PARAMS:
+            raise HoneycombError("The parameter {0} is invalid.".format(param))
+
+        path = ("bridge-domains", ("bridge-domain", "name", bd_name), param)
+        return BridgeDomainKeywords.\
+            _set_bd_properties(node, bd_name, path, value)
diff --git a/resources/libraries/python/HcAPIKwInterfaces.py b/resources/libraries/python/HcAPIKwInterfaces.py
new file mode 100644 (file)
index 0000000..ccdb5dd
--- /dev/null
@@ -0,0 +1,712 @@
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Keywords to manipulate interface configuration using Honeycomb REST API.
+
+The keywords make possible to put and get configuration data and to get
+operational data.
+"""
+
+
+from resources.libraries.python.HTTPRequest import HTTPCodes
+from resources.libraries.python.HoneycombSetup import HoneycombError
+from resources.libraries.python.HoneycombUtil import HoneycombUtil as HcUtil
+from resources.libraries.python.HoneycombUtil import DataRepresentation
+
+
+class InterfaceKeywords(object):
+    """Keywords for Interface manipulation.
+
+    Implements keywords which get configuration and operational data about
+    vpp interfaces and set the interface's parameters using Honeycomb REST API.
+    """
+
+    INTF_PARAMS = ("name", "description", "type", "enabled",
+                   "link-up-down-trap-enable")
+    IPV4_PARAMS = ("enabled", "forwarding", "mtu")
+    IPV6_PARAMS = ("enabled", "forwarding", "mtu", "dup-addr-detect-transmits")
+    IPV6_AUTOCONF_PARAMS = ("create-global-addresses",
+                            "create-temporary-addresses",
+                            "temporary-valid-lifetime",
+                            "temporary-preferred-lifetime")
+    ETH_PARAMS = ("mtu", )
+    ROUTING_PARAMS = ("vrf-id", )
+    VXLAN_PARAMS = ("src", "dst", "vni", "encap-vrf-id")
+    L2_PARAMS = ("bridge-domain", "split-horizon-group",
+                 "bridged-virtual-interface")
+
+    def __init__(self):
+        pass
+
+    @staticmethod
+    def _configure_interface(node, interface, data,
+                             data_representation=DataRepresentation.JSON):
+        """Send interface configuration data and check the response.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param data: Configuration data to be sent in PUT request.
+        :param data_representation: How the data is represented.
+        :type node: dict
+        :type interface: str
+        :type data: dict
+        :type data_representation: DataRepresentation
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If the status code in response on PUT is not
+        200 = OK.
+        """
+
+        status_code, resp = HcUtil.\
+            put_honeycomb_data(node, "config_vpp_interfaces", data,
+                               data_representation=data_representation)
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError(
+                "The configuration of interface '{0}' was not successful. "
+                "Status code: {1}.".format(interface, status_code))
+        return resp
+
+    @staticmethod
+    def get_all_interfaces_cfg_data(node):
+        """Get configuration data about all interfaces from Honeycomb.
+
+        :param node: Honeycomb node.
+        :type node: dict
+        :return: Configuration data about all interfaces from Honeycomb.
+        :rtype: list
+        :raises HoneycombError: If it is not possible to get configuration data.
+        """
+
+        status_code, resp = HcUtil.\
+            get_honeycomb_data(node, "config_vpp_interfaces")
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError(
+                "Not possible to get configuration information about the "
+                "interfaces. Status code: {0}.".format(status_code))
+        try:
+            return resp["interfaces"]["interface"]
+
+        except (KeyError, TypeError):
+            return []
+
+    @staticmethod
+    def get_interface_cfg_data(node, interface):
+        """Get configuration data about the given interface from Honeycomb.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :type node: dict
+        :type interface: str
+        :return: Configuration data about the given interface from Honeycomb.
+        :rtype: dict
+        """
+
+        intfs = InterfaceKeywords.get_all_interfaces_cfg_data(node)
+        for intf in intfs:
+            if intf["name"] == interface:
+                return intf
+        return {}
+
+    @staticmethod
+    def get_all_interfaces_oper_data(node):
+        """Get operational data about all interfaces from Honeycomb.
+
+        :param node: Honeycomb node.
+        :type node: dict
+        :return: Operational data about all interfaces from Honeycomb.
+        :rtype: list
+        :raises HoneycombError: If it is not possible to get operational data.
+        """
+
+        status_code, resp = HcUtil.\
+            get_honeycomb_data(node, "oper_vpp_interfaces")
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError(
+                "Not possible to get operational information about the "
+                "interfaces. Status code: {0}.".format(status_code))
+        try:
+            return resp["interfaces-state"]["interface"]
+
+        except (KeyError, TypeError):
+            return []
+
+    @staticmethod
+    def get_interface_oper_data(node, interface):
+        """Get operational data about the given interface from Honeycomb.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :type node: dict
+        :type interface: str
+        :return: Operational data about the given interface from Honeycomb.
+        :rtype: dict
+        """
+
+        intfs = InterfaceKeywords.get_all_interfaces_oper_data(node)
+        for intf in intfs:
+            if intf["name"] == interface:
+                return intf
+        return {}
+
+    @staticmethod
+    def _set_interface_properties(node, interface, path, new_value=None):
+        """Set interface properties.
+
+        This method reads interface configuration data, creates, changes or
+        removes the requested data and puts it back to Honeycomb.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param path:  Path to data we want to change / create / remove.
+        :param new_value: The new value to be set. If None, the item will be
+        removed.
+        :type node: dict
+        :type interface: str
+        :type path: tuple
+        :type new_value: str, dict or list
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If it is not possible to get or set the data.
+        """
+
+        status_code, resp = HcUtil.\
+            get_honeycomb_data(node, "config_vpp_interfaces")
+        if status_code != HTTPCodes.OK:
+            raise HoneycombError(
+                "Not possible to get configuration information about the "
+                "interfaces. Status code: {0}.".format(status_code))
+
+        if new_value:
+            new_data = HcUtil.set_item_value(resp, path, new_value)
+        else:
+            new_data = HcUtil.remove_item(resp, path)
+        return InterfaceKeywords._configure_interface(node, interface, new_data)
+
+    @staticmethod
+    def set_interface_state(node, interface, state="up"):
+        """Set VPP interface state.
+
+        The keyword changes the administration state of interface to up or down
+        depending on the parameter "state".
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param state: The requested state, only "up" and "down" are valid
+        values.
+        :type node: dict
+        :type interface: str
+        :type state: str
+        :return: Content of response.
+        :rtype: bytearray
+        :raises KeyError: If the argument "state" is nor "up" or "down".
+        :raises HoneycombError: If the interface is not present on the node.
+        """
+
+        intf_state = {"up": "true",
+                      "down": "false"}
+
+        path = ("interfaces", ("interface", "name", str(interface)), "enabled")
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, intf_state[state.lower()])
+
+    @staticmethod
+    def set_interface_up(node, interface):
+        """Set the administration state of VPP interface to up.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :type node: dict
+        :type interface: str
+        :return: Content of response
+        :rtype: bytearray
+        """
+
+        return InterfaceKeywords.set_interface_state(node, interface, "up")
+
+    @staticmethod
+    def set_interface_down(node, interface):
+        """Set the administration state of VPP interface to down.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :type node: dict
+        :type interface: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        return InterfaceKeywords.set_interface_state(node, interface, "down")
+
+    @staticmethod
+    def add_bridge_domain_to_interface(node, interface, bd_name,
+                                       split_horizon_group=None, bvi=None):
+        """Add a new bridge domain to an interface and set its parameters.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param bd_name: Bridge domain name.
+        :param split_horizon_group: Split-horizon group name.
+        :param bvi: The bridged virtual interface.
+        :type node: dict
+        :type interface: str
+        :type bd_name: str
+        :type split_horizon_group: str
+        :type bvi: str
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If the interface is not present on the node.
+        """
+
+        v3po_l2 = {"bridge-domain": str(bd_name)}
+        if split_horizon_group:
+            v3po_l2["split-horizon-group"] = str(split_horizon_group)
+        if bvi:
+            v3po_l2["bridged-virtual-interface"] = str(bvi)
+
+        path = ("interfaces", ("interface", "name", str(interface)), "v3po:l2")
+
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, v3po_l2)
+
+    @staticmethod
+    def configure_interface_base(node, interface, param, value):
+        """Configure the base parameters of interface.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param param: Parameter to configure (set, change, remove)
+        :param value: The value of parameter. If None, the parameter will be
+        removed.
+        :type node: dict
+        :type interface: str
+        :type param: str
+        :type value: str
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If the parameter is not valid.
+        """
+
+        if param not in InterfaceKeywords.INTF_PARAMS:
+            raise HoneycombError("The parameter {0} is invalid.".format(param))
+
+        path = ("interfaces", ("interface", "name", interface), param)
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, value)
+
+    @staticmethod
+    def configure_interface_ipv4(node, interface, param, value):
+        """Configure IPv4 parameters of interface
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param param: Parameter to configure (set, change, remove)
+        :param value: The value of parameter. If None, the parameter will be
+        removed.
+        :type node: dict
+        :type interface: str
+        :type param: str
+        :type value: str
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If the parameter is not valid.
+        """
+
+        if param not in InterfaceKeywords.IPV4_PARAMS:
+            raise HoneycombError("The parameter {0} is invalid.".format(param))
+
+        path = ("interfaces", ("interface", "name", interface),
+                "ietf-ip:ipv4", param)
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, value)
+
+    @staticmethod
+    def add_first_ipv4_address(node, interface, ip_addr, netmask):
+        """Add the first IPv4 address.
+
+        If there are any other addresses configured, they will be removed.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param ip_addr: IPv4 address to be set.
+        :param netmask: Netmask.
+        :type node: dict
+        :type interface: str
+        :type ip_addr: str
+        :type netmask: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4")
+        address = {"address": [{"ip": ip_addr, "netmask": netmask}, ]}
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, address)
+
+    @staticmethod
+    def add_ipv4_address(node, interface, ip_addr, netmask):
+        """Add IPv4 address.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param ip_addr: IPv4 address to be set.
+        :param netmask: Netmask.
+        :type node: dict
+        :type interface: str
+        :type ip_addr: str
+        :type netmask: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4",
+                "address")
+        address = [{"ip": ip_addr, "prefix-length": netmask}, ]
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, address)
+
+    @staticmethod
+    def remove_all_ipv4_addresses(node, interface):
+        """Remove all IPv4 addresses from interface.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :type node: dict
+        :type interface: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4",
+                "address")
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, None)
+
+    @staticmethod
+    def add_first_ipv4_neighbor(node, interface, ip_addr, link_layer_address):
+        """Add the first IPv4 neighbour.
+
+        If there are any other neighbours configured, they will be removed.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param ip_addr: IPv4 address of neighbour to be set.
+        :param link_layer_address: Link layer address.
+        :type node: dict
+        :type interface: str
+        :type ip_addr: str
+        :type link_layer_address: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4")
+        neighbor = {"neighbor": [{"ip": ip_addr,
+                                  "link-layer-address": link_layer_address}, ]}
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, neighbor)
+
+    @staticmethod
+    def add_ipv4_neighbor(node, interface, ip_addr, link_layer_address):
+        """Add the IPv4 neighbour.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param ip_addr: IPv4 address of neighbour to be set.
+        :param link_layer_address: Link layer address.
+        :type node: dict
+        :type interface: str
+        :type ip_addr: str
+        :type link_layer_address: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4",
+                "neighbor")
+        neighbor = [{"ip": ip_addr, "link-layer-address": link_layer_address}, ]
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, neighbor)
+
+    @staticmethod
+    def remove_all_ipv4_neighbors(node, interface):
+        """Remove all IPv4 neighbours.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :type node: dict
+        :type interface: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4",
+                "neighbor")
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, None)
+
+    @staticmethod
+    def configure_interface_ipv6(node, interface, param, value):
+        """Configure IPv6 parameters of interface
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param param: Parameter to configure (set, change, remove)
+        :param value: The value of parameter. If None, the parameter will be
+        removed.
+        :type node: dict
+        :type interface: str
+        :type param: str
+        :type value: str
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If the parameter is not valid.
+        """
+
+        if param in InterfaceKeywords.IPV6_PARAMS:
+            path = ("interfaces", ("interface", "name", interface),
+                    "ietf-ip:ipv6", param)
+        elif param in InterfaceKeywords.IPV6_AUTOCONF_PARAMS:
+            path = ("interfaces", ("interface", "name", interface),
+                    "ietf-ip:ipv6", "autoconf", param)
+        else:
+            raise HoneycombError("The parameter {0} is invalid.".format(param))
+
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, value)
+
+    @staticmethod
+    def add_first_ipv6_address(node, interface, ip_addr, prefix_len):
+        """Add the first IPv6 address.
+
+        If there are any other addresses configured, they will be removed.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param ip_addr: IPv6 address to be set.
+        :param prefix_len: Prefix length.
+        :type node: dict
+        :type interface: str
+        :type ip_addr: str
+        :type prefix_len: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6")
+        address = {"address": [{"ip": ip_addr, "prefix-length": prefix_len}, ]}
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, address)
+
+    @staticmethod
+    def add_ipv6_address(node, interface, ip_addr, prefix_len):
+        """Add IPv6 address.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param ip_addr: IPv6 address to be set.
+        :param prefix_len: Prefix length.
+        :type node: dict
+        :type interface: str
+        :type ip_addr: str
+        :type prefix_len: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6",
+                "address")
+        address = [{"ip": ip_addr, "prefix-length": prefix_len}, ]
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, address)
+
+    @staticmethod
+    def remove_all_ipv6_addresses(node, interface):
+        """Remove all IPv6 addresses from interface.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :type node: dict
+        :type interface: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6",
+                "address")
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, None)
+
+    @staticmethod
+    def add_first_ipv6_neighbor(node, interface, ip_addr, link_layer_address):
+        """Add the first IPv6 neighbour.
+
+        If there are any other neighbours configured, they will be removed.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param ip_addr: IPv6 address of neighbour to be set.
+        :param link_layer_address: Link layer address.
+        :type node: dict
+        :type interface: str
+        :type ip_addr: str
+        :type link_layer_address: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6")
+        neighbor = {"neighbor": [{"ip": ip_addr,
+                                  "link-layer-address": link_layer_address}, ]}
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, neighbor)
+
+    @staticmethod
+    def add_ipv6_neighbor(node, interface, ip_addr, link_layer_address):
+        """Add the IPv6 neighbour.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param ip_addr: IPv6 address of neighbour to be set.
+        :param link_layer_address: Link layer address.
+        :type node: dict
+        :type interface: str
+        :type ip_addr: str
+        :type link_layer_address: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6",
+                "neighbor")
+        neighbor = [{"ip": ip_addr, "link-layer-address": link_layer_address}, ]
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, neighbor)
+
+    @staticmethod
+    def remove_all_ipv6_neighbors(node, interface):
+        """Remove all IPv6 neighbours.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :type node: dict
+        :type interface: str
+        :return: Content of response.
+        :rtype: bytearray
+        """
+
+        path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6",
+                "neighbor")
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, None)
+
+    @staticmethod
+    def configure_interface_ethernet(node, interface, param, value):
+        """Configure the ethernet parameters of interface.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param param: Parameter to configure (set, change, remove)
+        :param value: The value of parameter. If None, the parameter will be
+        removed.
+        :type node: dict
+        :type interface: str
+        :type param: str
+        :type value: str
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If the parameter is not valid.
+        """
+
+        if param not in InterfaceKeywords.ETH_PARAMS:
+            raise HoneycombError("The parameter {0} is invalid.".format(param))
+        path = ("interfaces", ("interface", "name", interface), "v3po:ethernet",
+                param)
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, value)
+
+    @staticmethod
+    def configure_interface_routing(node, interface, param, value):
+        """Configure the routing parameters of interface.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param param: Parameter to configure (set, change, remove)
+        :param value: The value of parameter. If None, the parameter will be
+        removed.
+        :type node: dict
+        :type interface: str
+        :type param: str
+        :type value: str
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If the parameter is not valid.
+        """
+
+        if param not in InterfaceKeywords.ROUTING_PARAMS:
+            raise HoneycombError("The parameter {0} is invalid.".format(param))
+
+        path = ("interfaces", ("interface", "name", interface), "v3po:routing",
+                param)
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, value)
+
+    @staticmethod
+    def configure_interface_vxlan(node, interface, param, value):
+        """Configure the VxLAN parameters of interface.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param param: Parameter to configure (set, change, remove)
+        :param value: The value of parameter. If None, the parameter will be
+        removed.
+        :type node: dict
+        :type interface: str
+        :type param: str
+        :type value: str
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If the parameter is not valid.
+        """
+
+        if param not in InterfaceKeywords.VXLAN_PARAMS:
+            raise HoneycombError("The parameter {0} is invalid.".format(param))
+
+        path = ("interfaces", ("interface", "name", interface), "v3po:vxlan",
+                param)
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, value)
+
+    @staticmethod
+    def configure_interface_l2(node, interface, param, value):
+        """Configure the L2 parameters of interface.
+
+        :param node: Honeycomb node.
+        :param interface: The name of interface.
+        :param param: Parameter to configure (set, change, remove)
+        :param value: The value of parameter. If None, the parameter will be
+        removed.
+        :type node: dict
+        :type interface: str
+        :type param: str
+        :type value: str
+        :return: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If the parameter is not valid.
+        """
+
+        if param not in InterfaceKeywords.L2_PARAMS:
+            raise HoneycombError("The parameter {0} is invalid.".format(param))
+        path = ("interfaces", ("interface", "name", interface), "v3po:l2",
+                param)
+        return InterfaceKeywords._set_interface_properties(
+            node, interface, path, value)
diff --git a/resources/libraries/python/HoneycombAPIKeywords.py b/resources/libraries/python/HoneycombAPIKeywords.py
deleted file mode 100644 (file)
index 0369a32..0000000
+++ /dev/null
@@ -1,543 +0,0 @@
-# Copyright (c) 2016 Cisco and/or its affiliates.
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at:
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Keywords used with Honeycomb.
-
-There are implemented keywords which work with:
-- Honeycomb operations
-- VPP Interfaces
-- Bridge domains
-
-The keywords make possible to put and get configuration data and to get
-operational data.
-"""
-
-from json import dumps
-
-from robot.api import logger
-
-from resources.libraries.python.HTTPRequest import HTTPCodes
-from resources.libraries.python.HoneycombSetup import HoneycombError
-from resources.libraries.python.HoneycombUtil import HoneycombUtil as HcUtil
-from resources.libraries.python.HoneycombUtil import DataRepresentation
-
-
-class OperationsKeywords(object):
-    """Keywords which perform "operations" in Honeycomb.
-
-    The keywords in this class are not a part of a specific area in Honeycomb,
-    e.g.: interfaces or bridge domains, but they perform "operations" in any
-    area of Honeycomb.
-    """
-
-    def __init__(self):
-        pass
-
-    @staticmethod
-    def poll_oper_data(node):
-        """Poll operational data.
-
-        You can use this keyword when you configure something in Honeycomb and
-        you want configuration data to make effect immediately, e.g.:
-
-        | | Create Bridge Domain | ....
-        | | Add Bridge Domain To Interface | ....
-        | | Poll Oper Data | ....
-        | | ${br}= | Get Oper Info About Bridge Domain | ....
-
-        ..note:: This is not very reliable way how to poll operational data.
-        This keyword is only temporary workaround and will be removed when this
-        problem is solved in Honeycomb.
-        :param node: Honeycomb node.
-        :type: dict
-        :raises HoneycombError: If it is not possible to poll operational data.
-        """
-
-        status_code, _ = HcUtil.\
-            post_honeycomb_data(node, "poll_oper_data", data='',
-                                data_representation=DataRepresentation.NO_DATA,
-                                timeout=30)
-        if status_code != HTTPCodes.OK:
-            raise HoneycombError("It was not possible to poll operational data "
-                                 "on node {0}.".format(node['host']))
-
-
-class InterfaceKeywords(object):
-    """Keywords for Interface manipulation.
-
-    Implements keywords which get configuration and operational data about
-    vpp interfaces and set the interface's parameters using Honeycomb REST API.
-    """
-
-    def __init__(self):
-        pass
-
-    @staticmethod
-    def _configure_interface(node, interface, data,
-                             data_representation=DataRepresentation.JSON):
-        """Send interface configuration data and check the response.
-
-        :param node: Honeycomb node.
-        :param interface: The name of interface.
-        :param data: Configuration data to be sent in PUT request.
-        :param data_representation: How the data is represented.
-        :type node: dict
-        :type interface: str
-        :type data: str
-        :type data_representation: DataRepresentation
-        :return: Content of response.
-        :rtype: bytearray
-        :raises HoneycombError: If the status code in response on PUT is not
-        200 = OK.
-        """
-
-        status_code, resp = HcUtil.\
-            put_honeycomb_data(node, "config_vpp_interfaces", data,
-                               data_representation=data_representation)
-        if status_code != HTTPCodes.OK:
-            raise HoneycombError(
-                "The configuration of interface '{0}' was not successful. "
-                "Status code: {1}.".format(interface, status_code))
-        return resp
-
-    @staticmethod
-    def get_all_interfaces_cfg_data(node):
-        """Get configuration data about all interfaces from Honeycomb.
-
-        :param node: Honeycomb node.
-        :type node: dict
-        :return: Configuration data about all interfaces from Honeycomb.
-        :rtype: list
-        :raises HoneycombError: If it is not possible to get configuration data.
-        """
-
-        status_code, resp = HcUtil.get_honeycomb_data(node,
-                                                      "config_vpp_interfaces")
-        if status_code != HTTPCodes.OK:
-            raise HoneycombError(
-                "Not possible to get configuration information about the "
-                "interfaces. Status code: {0}.".format(status_code))
-        try:
-            intf = HcUtil.parse_json_response(resp, ("interfaces", "interface"))
-            return intf
-        except KeyError:
-            return []
-
-    @staticmethod
-    def get_interface_cfg_info(node, interface):
-        """Get configuration data about the given interface from Honeycomb.
-
-        :param node: Honeycomb node.
-        :param interface: The name of interface.
-        :type node: dict
-        :type interface: str
-        :return: Configuration data about the given interface from Honeycomb.
-        :rtype: dict
-        """
-
-        intfs = InterfaceKeywords.get_all_interfaces_cfg_data(node)
-        for intf in intfs:
-            if intf["name"] == interface:
-                return intf
-        return {}
-
-    @staticmethod
-    def get_all_interfaces_oper_info(node):
-        """Get operational data about all interfaces from Honeycomb.
-
-        :param node: Honeycomb node.
-        :type node: dict
-        :return: Operational data about all interfaces from Honeycomb.
-        :rtype: list
-        :raises HoneycombError: If it is not possible to get operational data.
-        """
-
-        status_code, resp = HcUtil.get_honeycomb_data(node,
-                                                      "oper_vpp_interfaces")
-        if status_code != HTTPCodes.OK:
-            raise HoneycombError(
-                "Not possible to get operational information about the "
-                "interfaces. Status code: {0}.".format(status_code))
-        try:
-            intf = HcUtil.parse_json_response(resp, ("interfaces-state",
-                                                     "interface"))
-            return intf
-        except KeyError:
-            return []
-
-    @staticmethod
-    def get_interface_oper_info(node, interface):
-        """Get operational data about the given interface from Honeycomb.
-
-        :param node: Honeycomb node.
-        :param interface: The name of interface.
-        :type node: dict
-        :type interface: str
-        :return: Operational data about the given interface from Honeycomb.
-        :rtype: dict
-        """
-
-        intfs = InterfaceKeywords.get_all_interfaces_oper_info(node)
-        for intf in intfs:
-            if intf["name"] == interface:
-                return intf
-        return {}
-
-    @staticmethod
-    def set_interface_state(node, interface, state="up"):
-        """Set VPP interface state.
-
-        The keyword changes the administration state of interface to up or down
-        depending on the parameter "state".
-
-        :param node: Honeycomb node.
-        :param interface: The name of interface.
-        :param state: The requested state, only "up" and "down" are valid
-        values.
-        :type node: dict
-        :type interface: str
-        :type state: str
-        :return: Content of response.
-        :rtype: bytearray
-        :raises KeyError: If the argument "state" is nor "up" or "down".
-        :raises HoneycombError: If the interface is not present on the node.
-        """
-
-        intf_state = {"up": "true",
-                      "down": "false"}
-        intfs = InterfaceKeywords.get_all_interfaces_cfg_data(node)
-        for intf in intfs:
-            if intf["name"] == interface:
-                intf["enabled"] = intf_state[state.lower()]
-                new_intf = {"interfaces": {"interface": intfs}}
-                return InterfaceKeywords._configure_interface(node, interface,
-                                                              dumps(new_intf))
-        raise HoneycombError("The interface '{0}' is not present on node "
-                             "'{1}'.".format(interface, node['host']))
-
-    @staticmethod
-    def set_interface_up(node, interface):
-        """Set the administration state of VPP interface to up.
-
-        :param node: Honeycomb node.
-        :param interface: The name of interface.
-        :type node: dict
-        :type interface: str
-        :return: Content of response
-        :rtype: bytearray
-        """
-
-        return InterfaceKeywords.set_interface_state(node, interface, "up")
-
-    @staticmethod
-    def set_interface_down(node, interface):
-        """Set the administration state of VPP interface to down.
-
-        :param node: Honeycomb node.
-        :param interface: The name of interface.
-        :type node: dict
-        :type interface: str
-        :return: Content of response.
-        :rtype: bytearray
-        """
-
-        return InterfaceKeywords.set_interface_state(node, interface, "down")
-
-    @staticmethod
-    def add_bridge_domain_to_interface(node, interface, bd_name,
-                                       split_horizon_group=None, bvi=None):
-        """Add a new bridge domain to an interface and set its parameters.
-
-        :param node: Honeycomb node.
-        :param interface: The name of interface.
-        :param bd_name: Bridge domain name.
-        :param split_horizon_group: Split-horizon group name.
-        :param bvi: The bridged virtual interface.
-        :type node: dict
-        :type interface: str
-        :type bd_name: str
-        :type split_horizon_group: str
-        :type bvi: str
-        :return: Content of response.
-        :rtype: bytearray
-        :raises HoneycombError: If the interface is not present on the node.
-        """
-
-        intfs = InterfaceKeywords.get_all_interfaces_cfg_data(node)
-        v3po_l2 = {"bridge-domain": str(bd_name)}
-        if split_horizon_group:
-            v3po_l2["split-horizon-group"] = str(split_horizon_group)
-        if bvi:
-            v3po_l2["bridged-virtual-interface"] = str(bvi)
-        for intf in intfs:
-            if intf["name"] == interface:
-                intf["v3po:l2"] = v3po_l2
-                new_intf = {"interfaces": {"interface": intfs}}
-                return InterfaceKeywords._configure_interface(node, interface,
-                                                              dumps(new_intf))
-        raise HoneycombError("The interface '{0}' is not present on node "
-                             "'{1}'.".format(interface, node['host']))
-
-
-class BridgeDomainKeywords(object):
-    """Keywords for Bridge domain manipulation.
-
-    Implements keywords which get configuration and operational data about
-    bridge domains and put the bridge domains' parameters using Honeycomb REST
-    API.
-    """
-
-    def __init__(self):
-        pass
-
-    @staticmethod
-    def _create_json_bridge_domain_info(name, **kwargs):
-        """Generate bridge domain information in the structure as it is expected
-        by Honeycomb.
-
-        The generated data structure is as follows:
-        {
-            "bridge-domains": {
-                "bridge-domain": [
-                    {
-                        "name": "bd_name",
-                        "flood": "false",
-                        "forward": "false",
-                        "learn": "false",
-                        "unknown-unicast-flood": "false",
-                        "arp-termination": "false"
-                    }
-                ]
-            }
-        }
-
-        :param name: The name of new bridge-domain.
-        :param kwargs: named arguments:
-            flood (bool): If True, flooding is enabled.
-            forward (bool): If True, packet forwarding is enabled.
-            learn (bool): If True, learning is enabled.
-            uu_flood (bool): If True, unknown unicast flooding is enabled.
-            arp_termination (bool): If True, ARP termination is enabled.
-        :type name: str
-        :type kwargs: dict
-        :return: Bridge domain information in format suitable for Honeycomb.
-        :rtype: dict
-        :raises KeyError: If at least one of kwargs items is missing.
-        """
-
-        brd_info = {
-            "bridge-domains": {
-                "bridge-domain": [
-                    {"name": name,
-                     "flood": str(kwargs["flood"]).lower(),
-                     "forward": str(kwargs["forward"]).lower(),
-                     "learn": str(kwargs["learn"]).lower(),
-                     "unknown-unicast-flood": str(kwargs["uu_flood"]).lower(),
-                     "arp-termination": str(kwargs["arp_termination"]).lower()},
-                ]
-            }
-        }
-
-        return brd_info
-
-    @staticmethod
-    def create_bridge_domain(node, name, flood=True, forward=True, learn=True,
-                             uu_flood=True, arp_termination=False):
-        """Create a bridge domain using Honeycomb.
-
-        This keyword adds a new bridge domain to the list of bridge domains and
-        sets its parameters. The existing bridge domains are untouched.
-        :param node: Node with Honeycomb where the bridge domain should be
-        created.
-        :param name: The name of new bridge-domain.
-        :param flood: If True, flooding is enabled.
-        :param forward: If True, packet forwarding is enabled.
-        :param learn: If True, learning is enabled.
-        :param uu_flood: If True, unknown unicast flooding is enabled.
-        :param arp_termination: If True, ARP termination is enabled.
-        :type node: dict
-        :type name: str
-        :type flood: bool
-        :type forward: bool
-        :type learn: bool
-        :type uu_flood: bool
-        :type arp_termination: bool
-        :raises HoneycombError: If the bridge domain already exists or it has
-        not been created.
-        """
-
-        existing_brds = BridgeDomainKeywords.\
-            get_all_bds_cfg_data(node, ignore_404=True)
-
-        for brd in existing_brds:
-            if brd["name"] == name:
-                raise HoneycombError("Bridge domain {0} already exists.".
-                                     format(name))
-
-        brd_info = BridgeDomainKeywords._create_json_bridge_domain_info(
-            name, flood=flood, forward=forward, learn=learn, uu_flood=uu_flood,
-            arp_termination=arp_termination)
-        for brd in existing_brds:
-            brd_info["bridge-domains"]["bridge-domain"].append(brd)
-
-        status_code, _ = HcUtil.put_honeycomb_data(node, "config_bridge_domain",
-                                                   dumps(brd_info))
-        if status_code != HTTPCodes.OK:
-            raise HoneycombError(
-                "Bridge domain {0} was not created. "
-                "Status code: {01}.".format(name, status_code))
-
-    @staticmethod
-    def get_all_bds_oper_data(node):
-        """Get operational data about all bridge domains from Honeycomb.
-
-        :param node: Honeycomb node.
-        :type node: dict
-        :return: Operational data about all bridge domains from Honeycomb.
-        :rtype: list
-        :raises HoneycombError: If it is not possible to get information about
-        the bridge domains.
-        """
-
-        status_code, resp = HcUtil.get_honeycomb_data(node,
-                                                      "oper_bridge_domains")
-        if status_code != HTTPCodes.OK:
-            raise HoneycombError(
-                "Not possible to get information about the bridge domains. "
-                "Status code: {0}.".format(status_code))
-        try:
-            br_domains = HcUtil.parse_json_response(resp, ("bridge-domains",
-                                                           "bridge-domain"))
-        except KeyError:
-            return []
-        return br_domains
-
-    @staticmethod
-    def get_bd_oper_data(node, name):
-        """Get operational data about the given bridge domain from Honeycomb.
-
-        :param node: Honeycomb node.
-        :param name: The name of bridge domain.
-        :type node: dict
-        :type name: str
-        :return: Operational data about the given bridge domain from Honeycomb.
-        :rtype: dict
-        """
-
-        br_domains = BridgeDomainKeywords.get_all_bds_oper_data(node)
-        for br_domain in br_domains:
-            if br_domain["name"] == name:
-                br_domain["name"] = br_domain["name"]
-                return br_domain
-        return {}
-
-    @staticmethod
-    def get_all_bds_cfg_data(node, ignore_404=False):
-        """Get configuration data about all bridge domains from Honeycomb.
-
-        :param node: Honeycomb node.
-        :param ignore_404: If True, the error 404 is ignored.
-        :type node: dict
-        :type ignore_404: bool
-        :return: Configuration data about all bridge domains from Honeycomb.
-        :rtype: list
-        :raises HoneycombError: If it is not possible to get information about
-        the bridge domains.
-        """
-
-        status_code, resp = HcUtil.get_honeycomb_data(node,
-                                                      "config_bridge_domain")
-        if status_code != HTTPCodes.OK:
-            if ignore_404 and status_code == HTTPCodes.NOT_FOUND:
-                br_domains = list()
-                logger.debug("Error 404 ignored")
-            else:
-                raise HoneycombError(
-                    "Not possible to get information about the bridge domains. "
-                    "Status code: {0}.".format(status_code))
-        else:
-            try:
-                br_domains = HcUtil.parse_json_response(resp, ("bridge-domains",
-                                                               "bridge-domain"))
-            except KeyError:
-                return []
-        return br_domains
-
-    @staticmethod
-    def get_bd_cfg_data(node, name):
-        """Get configuration data about the given bridge domain from Honeycomb.
-
-        :param node: Honeycomb node.
-        :param name: The name of bridge domain.
-        :type node: dict
-        :type name: str
-        :return: Configuration data about the given bridge domain from
-        Honeycomb.
-        :rtype: dict
-        """
-
-        br_domains = BridgeDomainKeywords.get_all_bds_cfg_data(node)
-        for br_domain in br_domains:
-            if br_domain["name"] == name:
-                return br_domain
-        return {}
-
-    @staticmethod
-    def delete_all_bridge_domains(node):
-        """Delete all bridge domains on Honeycomb node.
-
-        :param node: Honeycomb node.
-        :type node: dict
-        :return: Response from DELETE request.
-        :rtype: str
-        :raises HoneycombError: If it is not possible to delete all bridge
-        domains.
-        """
-
-        status_code, resp = HcUtil.delete_honeycomb_data(node,
-                                                         "config_bridge_domain")
-        if status_code != HTTPCodes.OK:
-            raise HoneycombError(
-                "Not possible to delete all bridge domains. "
-                "Status code: {0}.".format(status_code))
-        return resp
-
-    @staticmethod
-    def remove_bridge_domain(node, name):
-        """Remove one bridge domain from Honeycomb.
-
-        :param node: Honeycomb node.
-        :param name: Name of the bridge domain to be removed.
-        :type node: dict
-        :type name: str
-        :return: True if the bridge domain was removed.
-        :rtype: bool
-        :raises HoneycombError: If it is not possible to remove the bridge
-        domain.
-        """
-
-        br_domains = BridgeDomainKeywords.get_all_bds_cfg_data(node)
-        for br_domain in br_domains:
-            if br_domain["name"] == name:
-                br_domains.remove(br_domain)
-                brd_info = {"bridge-domains": {"bridge-domain": br_domains}}
-                status_code, _ = HcUtil.put_honeycomb_data(
-                    node, "config_bridge_domain", dumps(brd_info))
-                if status_code != HTTPCodes.OK:
-                    raise HoneycombError(
-                        "Bridge domain '{0}' was not deleted. "
-                        "Status code: {1}.".format(name, status_code))
-                return True
-
-        raise HoneycombError("Not possible to delete bridge domain '{0}'. The "
-                             "bridge domain was not found".format(name))
index bd2f308..979b501 100644 (file)
@@ -13,8 +13,6 @@
 
 """Implementation of keywords for Honeycomb setup."""
 
-from xml.etree import ElementTree as ET
-
 from robot.api import logger
 
 from resources.libraries.python.topology import NodeType
@@ -186,93 +184,3 @@ class HoneycombSetup(object):
                         logger.info("Honeycomb on node {0} has stopped".
                                     format(node['host']))
         return True
-
-    @staticmethod
-    def add_vpp_to_honeycomb_network_topology(nodes):
-        """Add vpp node to Honeycomb network topology.
-
-        :param nodes: All nodes in test topology.
-        :type nodes: dict
-        :return: Status code and response content from PUT requests.
-        :rtype: tuple
-        :raises HoneycombError: If a node was not added to Honeycomb topology.
-
-        Reads HTML path from template file config_topology_node.url.
-        Path to the node to be added, e.g.:
-        ("/restconf/config/network-topology:network-topology"
-         "/topology/topology-netconf/node/")
-        There must be "/" at the end, as generated node name is added at the
-        end.
-
-        Reads payload data from template file add_vpp_to_topology.xml.
-        Information about node as XML structure, e.g.:
-        <node xmlns="urn:TBD:params:xml:ns:yang:network-topology">
-            <node-id>
-                {vpp_host}
-            </node-id>
-            <host xmlns="urn:opendaylight:netconf-node-topology">
-                {vpp_ip}
-            </host>
-            <port xmlns="urn:opendaylight:netconf-node-topology">
-                {vpp_port}
-            </port>
-            <username xmlns="urn:opendaylight:netconf-node-topology">
-                {user}
-            </username>
-            <password xmlns="urn:opendaylight:netconf-node-topology">
-                {passwd}
-            </password>
-            <tcp-only xmlns="urn:opendaylight:netconf-node-topology">
-                false
-            </tcp-only>
-            <keepalive-delay xmlns="urn:opendaylight:netconf-node-topology">
-                0
-            </keepalive-delay>
-        </node>
-        NOTE: The placeholders:
-            {vpp_host}
-            {vpp_ip}
-            {vpp_port}
-            {user}
-            {passwd}
-        MUST be there as they are replaced by correct values.
-        """
-        path = HcUtil.read_path_from_url_file("config_topology_node")
-        try:
-            xml_data = ET.parse("{0}/add_vpp_to_topology.xml".
-                                format(Const.RESOURCES_TPL_HC))
-        except ET.ParseError as err:
-            raise HoneycombError(repr(err))
-        data = ET.tostring(xml_data.getroot())
-
-        headers = {"Content-Type": "application/xml"}
-
-        status_codes = []
-        responses = []
-        for node_name, node in nodes.items():
-            if node['type'] == NodeType.DUT:
-                try:
-                    payload = data.format(
-                        vpp_host=node_name,
-                        vpp_ip=node["host"],
-                        vpp_port=node['honeycomb']["netconf_port"],
-                        user=node['honeycomb']["user"],
-                        passwd=node['honeycomb']["passwd"])
-                    status_code, resp = HTTPRequest.put(
-                        node=node,
-                        path="{0}/{1}".format(path, node_name),
-                        headers=headers,
-                        payload=payload)
-                    if status_code != HTTPCodes.OK:
-                        raise HoneycombError(
-                            "VPP {0} was not added to topology. "
-                            "Status code: {1}.".format(node["host"],
-                                                       status_code))
-
-                    status_codes.append(status_code)
-                    responses.append(resp)
-
-                except HTTPRequestError as err:
-                    raise HoneycombError("VPP {0} was not added to topology.".
-                                         format(node["host"]), repr(err))
-        return status_codes, responses
index 7ffa643..644cf62 100644 (file)
@@ -22,7 +22,7 @@ be used directly in tests. Use keywords implemented in the module
 HoneycombAPIKeywords instead.
 """
 
-from json import loads
+from json import loads, dumps
 from enum import Enum, unique
 
 from robot.api import logger
@@ -42,7 +42,7 @@ class DataRepresentation(Enum):
 
 # Headers used in requests. Key - content representation, value - header.
 HEADERS = {DataRepresentation.NO_DATA:
-               {},  # Must be empty.
+               {},  # It must be empty dictionary.
            DataRepresentation.JSON:
                {"Content-Type": "application/json",
                 "Accept": "text/plain"},
@@ -136,51 +136,153 @@ class HoneycombUtil(object):
         return path
 
     @staticmethod
-    def parse_json_response(response, path=None):
-        """Parse data from response string in JSON format according to given
-        path.
-
-        :param response: JSON formatted string.
-        :param path: Path to navigate down the data structure.
-        :type response: str
+    def find_item(data, path):
+        """Find a data item (single leaf or sub-tree) in data received from
+        Honeycomb REST API.
+
+        Path format:
+        The path is a tuple with items navigating to requested data. The items
+        can be strings or tuples:
+        - string item represents a dictionary key in data,
+        - tuple item represents list item in data.
+
+        Example:
+        data = \
+        {
+            "interfaces": {
+                "interface": [
+                    {
+                        "name": "GigabitEthernet0/8/0",
+                        "enabled": "true",
+                        "type": "iana-if-type:ethernetCsmacd",
+                    },
+                    {
+                        "name": "local0",
+                        "enabled": "false",
+                        "type": "iana-if-type:ethernetCsmacd",
+                    }
+                ]
+            }
+        }
+
+        path = ("interfaces", ("interface", "name", "local0"), "enabled")
+        This path points to "false".
+
+        The tuple ("interface", "name", "local0") consists of:
+        index 0 - dictionary key pointing to a list,
+        index 1 - key which identifies an item in the list, it is also marked as
+                  the key in corresponding yang file.
+        index 2 - key value.
+
+        :param data: Data received from Honeycomb REST API.
+        :param path: Path to data we want to find.
+        :type data: dict
         :type path: tuple
-        :return: JSON dictionary/list tree.
-        :rtype: list
+        :return: Data represented by path.
+        :rtype: str, dict, or list
+        :raises HoneycombError: If the data has not been found.
         """
-        data = loads(response)
 
-        if path:
-            data = HoneycombUtil._parse_json_tree(data, path)
-            if not isinstance(data, list):
-                data = [data, ]
+        for path_item in path:
+            try:
+                if isinstance(path_item, str):
+                    data = data[path_item]
+                elif isinstance(path_item, tuple):
+                    for data_item in data[path_item[0]]:
+                        if data_item[path_item[1]] == path_item[2]:
+                            data = data_item
+            except KeyError as err:
+                raise HoneycombError("Data not found: {0}".format(err))
 
         return data
 
     @staticmethod
-    def _parse_json_tree(data, path):
-        """Retrieve data addressed by path from python representation of JSON
-        object.
+    def remove_item(data, path):
+        """Remove a data item (single leaf or sub-tree) in data received from
+        Honeycomb REST API.
 
-        :param data: Parsed JSON dictionary tree.
-        :param path: Path to navigate down the dictionary tree.
+        :param data: Data received from Honeycomb REST API.
+        :param path: Path to data we want to remove.
         :type data: dict
         :type path: tuple
-        :return: Data from specified path.
-        :rtype: list, dict or str
+        :return: Original data without removed part.
+        :rtype: dict
         """
 
-        count = 0
-        for key in path:
-            if isinstance(data, dict):
-                data = data[key]
-                count += 1
-            elif isinstance(data, list):
-                result = []
-                for item in data:
-                    result.append(HoneycombUtil._parse_json_tree(item,
-                                                                 path[count:]))
-                    return result
-        return data
+        origin_data = previous_data = data
+        try:
+            for path_item in path:
+                previous_data = data
+                if isinstance(path_item, str):
+                    data = data[path_item]
+                elif isinstance(path_item, tuple):
+                    for data_item in data[path_item[0]]:
+                        if data_item[path_item[1]] == path_item[2]:
+                            data = data_item
+        except KeyError as err:
+            logger.debug("Data not found: {0}".format(err))
+            return origin_data
+
+        if isinstance(path[-1], str):
+            previous_data.pop(path[-1])
+        elif isinstance(path[-1], tuple):
+            previous_data[path[-1][0]].remove(data)
+            if not previous_data[path[-1][0]]:
+                previous_data.pop(path[-1][0])
+
+        return origin_data
+
+    @staticmethod
+    def set_item_value(data, path, new_value):
+        """Set or change the value (single leaf or sub-tree) in data received
+        from Honeycomb REST API.
+
+        If the item is not present in the data structure, it is created.
+
+        :param data: Data received from Honeycomb REST API.
+        :param path: Path to data we want to change or create.
+        :param new_value: The value to be set.
+        :type data: dict
+        :type path: tuple
+        :type new_value: str, dict or list
+        :return: Original data with the new value.
+        :rtype: dict
+        """
+
+        origin_data = data
+        for path_item in path[:-1]:
+            if isinstance(path_item, str):
+                try:
+                    data = data[path_item]
+                except KeyError:
+                    data[path_item] = {}
+                    data = data[path_item]
+            elif isinstance(path_item, tuple):
+                try:
+                    flag = False
+                    index = 0
+                    for data_item in data[path_item[0]]:
+                        if data_item[path_item[1]] == path_item[2]:
+                            data = data[path_item[0]][index]
+                            flag = True
+                            break
+                        index += 1
+                    if not flag:
+                        data[path_item[0]].append({path_item[1]: path_item[2]})
+                        data = data[path_item[0]][-1]
+                except KeyError:
+                    data[path_item] = []
+
+        if not path[-1] in data.keys():
+            data[path[-1]] = {}
+
+        if isinstance(new_value, list) and isinstance(data[path[-1]], list):
+            for value in new_value:
+                data[path[-1]].append(value)
+        else:
+            data[path[-1]] = new_value
+
+        return origin_data
 
     @staticmethod
     def get_honeycomb_data(node, url_file):
@@ -196,7 +298,8 @@ class HoneycombUtil(object):
         """
 
         path = HoneycombUtil.read_path_from_url_file(url_file)
-        return HTTPRequest.get(node, path)
+        status_code, resp = HTTPRequest.get(node, path)
+        return status_code, loads(resp)
 
     @staticmethod
     def put_honeycomb_data(node, url_file, data,
@@ -211,7 +314,7 @@ class HoneycombUtil(object):
         :param data_representation: How the data is represented.
         :type node: dict
         :type url_file: str
-        :type data: str
+        :type data: dict, str
         :type data_representation: DataRepresentation
         :return: Status code and content of response.
         :rtype: tuple
@@ -224,6 +327,8 @@ class HoneycombUtil(object):
         except AttributeError as err:
             raise HoneycombError("Wrong data representation: {0}.".
                                  format(data_representation), repr(err))
+        if data_representation == DataRepresentation.JSON:
+            data = dumps(data)
 
         path = HoneycombUtil.read_path_from_url_file(url_file)
         return HTTPRequest.put(node=node, path=path, headers=header,
@@ -244,7 +349,7 @@ class HoneycombUtil(object):
         giving up.
         :type node: dict
         :type url_file: str
-        :type data: str
+        :type data: dict, str
         :type data_representation: DataRepresentation
         :type timeout: int
         :return: Status code and content of response.
@@ -258,6 +363,8 @@ class HoneycombUtil(object):
         except AttributeError as err:
             raise HoneycombError("Wrong data representation: {0}.".
                                  format(data_representation), repr(err))
+        if data_representation == DataRepresentation.JSON:
+            data = dumps(data)
 
         path = HoneycombUtil.read_path_from_url_file(url_file)
         return HTTPRequest.post(node=node, path=path, headers=header,