diff options
Diffstat (limited to 'resources/libraries/python/honeycomb')
-rw-r--r-- | resources/libraries/python/honeycomb/HcAPIKwBridgeDomain.py | 330 | ||||
-rw-r--r-- | resources/libraries/python/honeycomb/HcAPIKwInterfaces.py | 713 | ||||
-rw-r--r-- | resources/libraries/python/honeycomb/HoneycombSetup.py | 187 | ||||
-rw-r--r-- | resources/libraries/python/honeycomb/HoneycombUtil.py | 387 | ||||
-rw-r--r-- | resources/libraries/python/honeycomb/__init__.py | 16 |
5 files changed, 1633 insertions, 0 deletions
diff --git a/resources/libraries/python/honeycomb/HcAPIKwBridgeDomain.py b/resources/libraries/python/honeycomb/HcAPIKwBridgeDomain.py new file mode 100644 index 0000000000..0906d5c762 --- /dev/null +++ b/resources/libraries/python/honeycomb/HcAPIKwBridgeDomain.py @@ -0,0 +1,330 @@ +# 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.honeycomb.HoneycombSetup import HoneycombError +from resources.libraries.python.honeycomb.HoneycombUtil \ + import DataRepresentation +from resources.libraries.python.honeycomb.HoneycombUtil \ + import HoneycombUtil as HcUtil + + +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/honeycomb/HcAPIKwInterfaces.py b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py new file mode 100644 index 0000000000..8f6819ef5e --- /dev/null +++ b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py @@ -0,0 +1,713 @@ +# 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.honeycomb.HoneycombSetup import HoneycombError +from resources.libraries.python.honeycomb.HoneycombUtil \ + import DataRepresentation +from resources.libraries.python.honeycomb.HoneycombUtil \ + import HoneycombUtil as HcUtil + + +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/honeycomb/HoneycombSetup.py b/resources/libraries/python/honeycomb/HoneycombSetup.py new file mode 100644 index 0000000000..e9c1295260 --- /dev/null +++ b/resources/libraries/python/honeycomb/HoneycombSetup.py @@ -0,0 +1,187 @@ +# 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. + +"""Implementation of keywords for Honeycomb setup.""" + +from robot.api import logger + +from resources.libraries.python.HTTPRequest import HTTPRequest, HTTPCodes, \ + HTTPRequestError +from resources.libraries.python.constants import Constants as Const +from resources.libraries.python.honeycomb.HoneycombUtil import HoneycombError +from resources.libraries.python.honeycomb.HoneycombUtil \ + import HoneycombUtil as HcUtil +from resources.libraries.python.ssh import SSH +from resources.libraries.python.topology import NodeType + + +class HoneycombSetup(object): + """Implements keywords for Honeycomb setup. + + The keywords implemented in this class make possible to: + - start Honeycomb, + - stop Honeycomb, + - check the Honeycomb start-up state, + - check the Honeycomb shutdown state, + - add VPP to the topology. + """ + + def __init__(self): + pass + + @staticmethod + def start_honeycomb_on_duts(*nodes): + """Start Honeycomb on specified DUT nodes. + + This keyword starts the Honeycomb service on specified DUTs. + The keyword just starts the Honeycomb and does not check its startup + state. Use the keyword "Check Honeycomb Startup State" to check if the + Honeycomb is up and running. + Honeycomb must be installed in "/opt" directory, otherwise the start + will fail. + :param nodes: List of nodes to start Honeycomb on. + :type nodes: list + :raises HoneycombError: If Honeycomb fails to start. + """ + logger.console("Starting Honeycomb service ...") + + cmd = "{0}/start".format(Const.REMOTE_HC_DIR) + + for node in nodes: + if node['type'] == NodeType.DUT: + ssh = SSH() + ssh.connect(node) + (ret_code, _, _) = ssh.exec_command_sudo(cmd) + if int(ret_code) != 0: + raise HoneycombError('Node {0} failed to start Honeycomb.'. + format(node['host'])) + else: + logger.info("Starting the Honeycomb service on node {0} is " + "in progress ...".format(node['host'])) + + @staticmethod + def stop_honeycomb_on_duts(*nodes): + """Stop the Honeycomb service on specified DUT nodes. + + This keyword stops the Honeycomb service on specified nodes. It just + stops the Honeycomb and does not check its shutdown state. Use the + keyword "Check Honeycomb Shutdown State" to check if Honeycomb has + stopped. + :param nodes: List of nodes to stop Honeycomb on. + :type nodes: list + :raises HoneycombError: If Honeycomb failed to stop. + """ + logger.console("Shutting down Honeycomb service ...") + + cmd = "{0}/stop".format(Const.REMOTE_HC_DIR) + errors = [] + + for node in nodes: + if node['type'] == NodeType.DUT: + ssh = SSH() + ssh.connect(node) + (ret_code, _, _) = ssh.exec_command_sudo(cmd) + if int(ret_code) != 0: + errors.append(node['host']) + else: + logger.info("Stopping the Honeycomb service on node {0} is " + "in progress ...".format(node['host'])) + if errors: + raise HoneycombError('Node(s) {0} failed to stop Honeycomb.'. + format(errors)) + + @staticmethod + def check_honeycomb_startup_state(*nodes): + """Check state of Honeycomb service during startup on specified nodes. + + Reads html path from template file oper_vpp_version.url. + + Honeycomb nodes reply with connection refused or the following status + codes depending on startup progress: codes 200, 401, 403, 404, 500, 503 + + :param nodes: List of DUT nodes starting Honeycomb. + :type nodes: list + :return: True if all GETs returned code 200(OK). + :rtype bool + """ + path = HcUtil.read_path_from_url_file("oper_vpp_version") + expected_status_codes = (HTTPCodes.UNAUTHORIZED, + HTTPCodes.FORBIDDEN, + HTTPCodes.NOT_FOUND, + HTTPCodes.SERVICE_UNAVAILABLE, + HTTPCodes.INTERNAL_SERVER_ERROR) + + for node in nodes: + if node['type'] == NodeType.DUT: + status_code, _ = HTTPRequest.get(node, path, timeout=10, + enable_logging=False) + if status_code == HTTPCodes.OK: + logger.info("Honeycomb on node {0} is up and running". + format(node['host'])) + elif status_code in expected_status_codes: + if status_code == HTTPCodes.UNAUTHORIZED: + logger.info('Unauthorized. If this triggers keyword ' + 'timeout, verify Honeycomb username and ' + 'password.') + raise HoneycombError('Honeycomb on node {0} running but ' + 'not yet ready.'.format(node['host']), + enable_logging=False) + else: + raise HoneycombError('Unexpected return code: {0}.'. + format(status_code)) + return True + + @staticmethod + def check_honeycomb_shutdown_state(*nodes): + """Check state of Honeycomb service during shutdown on specified nodes. + + Honeycomb nodes reply with connection refused or the following status + codes depending on shutdown progress: codes 200, 404. + + :param nodes: List of DUT nodes stopping Honeycomb. + :type nodes: list + :return: True if all GETs fail to connect. + :rtype bool + """ + cmd = "ps -ef | grep -v grep | grep karaf" + for node in nodes: + if node['type'] == NodeType.DUT: + try: + status_code, _ = HTTPRequest.get(node, '/index.html', + timeout=5, + enable_logging=False) + if status_code == HTTPCodes.OK: + raise HoneycombError('Honeycomb on node {0} is still ' + 'running.'.format(node['host']), + enable_logging=False) + elif status_code == HTTPCodes.NOT_FOUND: + raise HoneycombError('Honeycomb on node {0} is shutting' + ' down.'.format(node['host']), + enable_logging=False) + else: + raise HoneycombError('Unexpected return code: {0}.'. + format(status_code)) + except HTTPRequestError: + logger.debug('Connection refused, checking the process ' + 'state ...') + ssh = SSH() + ssh.connect(node) + (ret_code, _, _) = ssh.exec_command_sudo(cmd) + if ret_code == 0: + raise HoneycombError('Honeycomb on node {0} is still ' + 'running.'.format(node['host']), + enable_logging=False) + else: + logger.info("Honeycomb on node {0} has stopped". + format(node['host'])) + return True diff --git a/resources/libraries/python/honeycomb/HoneycombUtil.py b/resources/libraries/python/honeycomb/HoneycombUtil.py new file mode 100644 index 0000000000..644cf62c43 --- /dev/null +++ b/resources/libraries/python/honeycomb/HoneycombUtil.py @@ -0,0 +1,387 @@ +# 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. + +"""Implementation of low level functionality used in communication with +Honeycomb. + +Exception HoneycombError is used in all methods and in all modules with +Honeycomb keywords. + +Class HoneycombUtil implements methods used by Honeycomb keywords. They must not +be used directly in tests. Use keywords implemented in the module +HoneycombAPIKeywords instead. +""" + +from json import loads, dumps +from enum import Enum, unique + +from robot.api import logger + +from resources.libraries.python.HTTPRequest import HTTPRequest +from resources.libraries.python.constants import Constants as Const + + +@unique +class DataRepresentation(Enum): + """Representation of data sent by PUT and POST requests.""" + NO_DATA = 0 + JSON = 1 + XML = 2 + TXT = 3 + + +# Headers used in requests. Key - content representation, value - header. +HEADERS = {DataRepresentation.NO_DATA: + {}, # It must be empty dictionary. + DataRepresentation.JSON: + {"Content-Type": "application/json", + "Accept": "text/plain"}, + DataRepresentation.XML: + {"Content-Type": "application/xml", + "Accept": "text/plain"}, + DataRepresentation.TXT: + {"Content-Type": "text/plain", + "Accept": "text/plain"} + } + + +class HoneycombError(Exception): + + """Exception(s) raised by methods working with Honeycomb. + + When raising this exception, put this information to the message in this + order: + - short description of the encountered problem (parameter msg), + - relevant messages if there are any collected, e.g., from caught + exception (optional parameter details), + - relevant data if there are any collected (optional parameter details). + The logging is performed on two levels: 1. error - short description of the + problem; 2. debug - detailed information. + """ + + def __init__(self, msg, details='', enable_logging=True): + """Sets the exception message and enables / disables logging. + + It is not wanted to log errors when using these keywords together + with keywords like "Wait until keyword succeeds". So you can disable + logging by setting enable_logging to False. + + :param msg: Message to be displayed and logged. + :param enable_logging: When True, logging is enabled, otherwise + logging is disabled. + :type msg: str + :type enable_logging: bool + """ + super(HoneycombError, self).__init__() + self._msg = "{0}: {1}".format(self.__class__.__name__, msg) + self._details = details + if enable_logging: + logger.error(self._msg) + logger.debug(self._details) + + def __repr__(self): + return repr(self._msg) + + def __str__(self): + return str(self._msg) + + +class HoneycombUtil(object): + """Implements low level functionality used in communication with Honeycomb. + + There are implemented methods to get, put and delete data to/from Honeycomb. + They are based on functionality implemented in the module HTTPRequests which + uses HTTP requests GET, PUT, POST and DELETE to communicate with Honeycomb. + + It is possible to PUT the data represented as XML or JSON structures or as + plain text. + Data received in the response of GET are always represented as a JSON + structure. + + There are also two supportive methods implemented: + - read_path_from_url_file which reads URL file and returns a path (see + docs/honeycomb_url_files.rst). + - parse_json_response which parses data from response in JSON representation + according to given path. + """ + + def __init__(self): + pass + + @staticmethod + def read_path_from_url_file(url_file): + """Read path from *.url file. + + For more information about *.url file see docs/honeycomb_url_files.rst + :param url_file: URL file. The argument contains only the name of file + without extension, not the full path. + :type url_file: str + :return: Requested path. + :rtype: str + """ + + url_file = "{0}/{1}.url".format(Const.RESOURCES_TPL_HC, url_file) + with open(url_file) as template: + path = template.readline() + return path + + @staticmethod + 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: Data represented by path. + :rtype: str, dict, or list + :raises HoneycombError: If the data has not been found. + """ + + 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 remove_item(data, path): + """Remove a data item (single leaf or sub-tree) in data received from + Honeycomb REST API. + + :param data: Data received from Honeycomb REST API. + :param path: Path to data we want to remove. + :type data: dict + :type path: tuple + :return: Original data without removed part. + :rtype: dict + """ + + 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): + """Retrieve data from Honeycomb according to given URL. + + :param node: Honeycomb node. + :param url_file: URL file. The argument contains only the name of file + without extension, not the full path. + :type node: dict + :type url_file: str + :return: Status code and content of response. + :rtype tuple + """ + + path = HoneycombUtil.read_path_from_url_file(url_file) + status_code, resp = HTTPRequest.get(node, path) + return status_code, loads(resp) + + @staticmethod + def put_honeycomb_data(node, url_file, data, + data_representation=DataRepresentation.JSON): + """Send configuration data using PUT request and return the status code + and response content. + + :param node: Honeycomb node. + :param url_file: URL file. The argument contains only the name of file + without extension, not the full path. + :param data: Configuration data to be sent to Honeycomb. + :param data_representation: How the data is represented. + :type node: dict + :type url_file: str + :type data: dict, str + :type data_representation: DataRepresentation + :return: Status code and content of response. + :rtype: tuple + :raises HoneycombError: If the given data representation is not defined + in HEADERS. + """ + + try: + header = HEADERS[data_representation] + 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, + payload=data) + + @staticmethod + def post_honeycomb_data(node, url_file, data=None, + data_representation=DataRepresentation.JSON, + timeout=10): + """Send a POST request and return the status code and response content. + + :param node: Honeycomb node. + :param url_file: URL file. The argument contains only the name of file + without extension, not the full path. + :param data: Configuration data to be sent to Honeycomb. + :param data_representation: How the data is represented. + :param timeout: How long to wait for the server to send data before + giving up. + :type node: dict + :type url_file: str + :type data: dict, str + :type data_representation: DataRepresentation + :type timeout: int + :return: Status code and content of response. + :rtype: tuple + :raises HoneycombError: If the given data representation is not defined + in HEADERS. + """ + + try: + header = HEADERS[data_representation] + 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, + payload=data, timeout=timeout) + + @staticmethod + def delete_honeycomb_data(node, url_file): + """Delete data from Honeycomb according to given URL. + + :param node: Honeycomb node. + :param url_file: URL file. The argument contains only the name of file + without extension, not the full path. + :type node: dict + :type url_file: str + :return: Status code and content of response. + :rtype tuple + """ + + path = HoneycombUtil.read_path_from_url_file(url_file) + return HTTPRequest.delete(node, path) diff --git a/resources/libraries/python/honeycomb/__init__.py b/resources/libraries/python/honeycomb/__init__.py new file mode 100644 index 0000000000..c35326d604 --- /dev/null +++ b/resources/libraries/python/honeycomb/__init__.py @@ -0,0 +1,16 @@ +# 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. + +""" +__init__ file for directory resources/libraries/python/honeycomb +""" |