From 5a02dd13563a5c67e336f04eb526cbea206da29b Mon Sep 17 00:00:00 2001 From: selias Date: Fri, 29 Sep 2017 15:31:06 +0200 Subject: CSIT-811 HC Test: BGP tests - IPv4 CRUD Tests configure BGP neighbor peers and simple routes for these peers. Change-Id: I5102986d710551a451e838d934cc9650bcd38a60 Signed-off-by: selias --- resources/libraries/python/honeycomb/BGP.py | 301 +++++++++++++++++++++ .../libraries/python/honeycomb/HoneycombSetup.py | 38 +-- resources/libraries/python/honeycomb/Routing.py | 2 +- resources/libraries/robot/honeycomb/bgp.robot | 173 ++++++++++++ 4 files changed, 497 insertions(+), 17 deletions(-) create mode 100644 resources/libraries/python/honeycomb/BGP.py create mode 100644 resources/libraries/robot/honeycomb/bgp.robot (limited to 'resources/libraries') diff --git a/resources/libraries/python/honeycomb/BGP.py b/resources/libraries/python/honeycomb/BGP.py new file mode 100644 index 0000000000..ca50cc3120 --- /dev/null +++ b/resources/libraries/python/honeycomb/BGP.py @@ -0,0 +1,301 @@ +# Copyright (c) 2017 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 BGP configuration using Honeycomb REST API.""" + +from resources.libraries.python.HTTPRequest import HTTPCodes +from resources.libraries.python.honeycomb.HoneycombSetup import HoneycombError +from resources.libraries.python.honeycomb.HoneycombUtil \ + import HoneycombUtil as HcUtil + + +class BGPKeywords(object): + """Keywords to manipulate BGP configuration. + + Implements keywords which read configuration and operational data for + the BGP feature, and configure BGP parameters using Honeycomb REST API. + """ + + def __init__(self): + pass + + @staticmethod + def _configure_bgp_peer(node, path, data=None): + """Send BGP peer configuration data and check the response. + + :param node: Honeycomb node. + :param path: Additional path to append to the base BGP config path. + :param data: Configuration data to be sent in PUT request. + :type node: dict + :type path: str + :type data: dict + :returns: Content of response. + :rtype: bytearray + :raises HoneycombError: If the status code in response on PUT is not + 200 = OK or 201 = ACCEPTED. + """ + + if data is None: + status_code, resp = HcUtil. \ + delete_honeycomb_data(node, "config_bgp_peer", path) + else: + status_code, resp = HcUtil.\ + put_honeycomb_data(node, "config_bgp_peer", data, path) + if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): + raise HoneycombError( + "The configuration of BGP peer was not successful. " + "Status code: {0}.".format(status_code)) + return resp + + @staticmethod + def _configure_bgp_route(node, path, data=None): + """Send BGP route configuration data and check the response. + + :param node: Honeycomb node. + :param path: Additional path to append to the base BGP config path. + :param data: Configuration data to be sent in PUT request. + :type node: dict + :type path: str + :type data: dict + :returns: Content of response. + :rtype: bytearray + :raises HoneycombError: If the status code in response on PUT is not + 200 = OK or 201 = ACCEPTED. + """ + + if data is None: + status_code, resp = HcUtil. \ + delete_honeycomb_data(node, "config_bgp_route", path) + else: + status_code, resp = HcUtil. \ + put_honeycomb_data(node, "config_bgp_route", data, path) + if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): + raise HoneycombError( + "The configuration of BGP route was not successful. " + "Status code: {0}.".format(status_code)) + return resp + + @staticmethod + def get_full_bgp_configuration(node): + """Get BGP configuration from the node. + + :param node: Honeycomb node. + :type node: dict + :returns: BGP configuration data. + :rtype: dict + """ + + status_code, resp = HcUtil. \ + get_honeycomb_data(node, "config_bgp_peer") + if status_code != HTTPCodes.OK: + raise HoneycombError( + "Not possible to get configuration information about BGP." + " Status code: {0}.".format(status_code)) + return resp + + @staticmethod + def get_bgp_peer(node, address): + """Get BGP configuration of the specified peer from the node. + + :param node: Honeycomb node. + :param address: IP address of the peer. + :type node: dict + :type address: str + :returns: BGP peer configuration data. + :rtype: dict + """ + + path = "bgp-openconfig-extensions:neighbors/" \ + "neighbor/{0}".format(address) + status_code, resp = HcUtil. \ + get_honeycomb_data(node, "config_bgp_peer", path) + if status_code != HTTPCodes.OK: + raise HoneycombError( + "Not possible to get configuration information about the BGP" + " peer. Status code: {0}.".format(status_code)) + return resp + + @staticmethod + def add_bgp_peer(node, address, data): + """Configure a BGP peer on the node. + + :param node: Honeycomb node. + :param address: IP address of the peer. + :param data: Peer configuration data. + :type node: dict + :type address: str + :type data: dict + :returns: Content of response. + :rtype: bytearray + """ + + path = "bgp-openconfig-extensions:neighbors/neighbor/{address}".format( + address=address) + return BGPKeywords._configure_bgp_peer(node, path, data) + + @staticmethod + def remove_bgp_peer(node, address): + """Remove a BGP peer from the configuration. + + :param node: Honeycomb node. + :param address: IP address of the peer. + :type node: dict + :type address: str + :returns: Content of response. + :rtype: bytearray + """ + + path = "bgp-openconfig-extensions:neighbors/neighbor/{address}".format( + address=address) + return BGPKeywords._configure_bgp_peer(node, path) + + @staticmethod + def configure_bgp_route(node, peer_address, data, route_address, + index, ip_version): + """Configure a route for the BGP peer specified by peer IP address. + + :param node: Honeycomb node. + :param peer_address: IP address of the BGP peer. + :param data: Route configuration data. + :param route_address: IP address of the route. + :param index: Index number of the route within specified peer. + :param ip_version: IP protocol version. ipv4 or ipv6 + :type node: dict + :type peer_address: str + :type data: dict + :type route_address: str + :type index: int + :type ip_version: str + :returns: Content of response. + :rtype: bytearray + """ + + route_address = route_address.replace("/", "%2F") + + if ip_version.lower() == "ipv4": + path = "{0}/tables/bgp-types:ipv4-address-family/" \ + "bgp-types:unicast-subsequent-address-family/" \ + "bgp-inet:ipv4-routes/ipv4-route/{1}/{2}" \ + .format(peer_address, route_address, index) + else: + path = "{0}/tables/bgp-types:ipv6-address-family/" \ + "bgp-types:unicast-subsequent-address-family/" \ + "bgp-inet:ipv6-routes/ipv6-route/{1}/{2}" \ + .format(peer_address, route_address, index) + + return BGPKeywords._configure_bgp_route(node, path, data) + + @staticmethod + def get_bgp_route(node, peer_address, route_address, index, ip_version): + """Get all BGP peers from operational data. + + :param node: Honeycomb node. + :param peer_address: IP address of the BGP peer. + :param route_address: IP address of the route. + :param index: Index number of the route within specified peer. + :param ip_version: IP protocol version. ipv4 or ipv6 + :type node: dict + :type peer_address: str + :type route_address: str + :type index: int + :type ip_version: str + :returns: Content of response. + :rtype: bytearray + :raises HoneycombError: If the operation fails. + """ + + route_address = route_address.replace("/", "%2F") + + if ip_version.lower() == "ipv4": + path = "{0}/tables/bgp-types:ipv4-address-family/" \ + "bgp-types:unicast-subsequent-address-family/" \ + "bgp-inet:ipv4-routes/ipv4-route/{1}/{2}" \ + .format(peer_address, route_address, index) + else: + path = "{0}/tables/bgp-types:ipv6-address-family/" \ + "bgp-types:unicast-subsequent-address-family/" \ + "bgp-inet:ipv6-routes/ipv6-route/{1}/{2}" \ + .format(peer_address, route_address, index) + status_code, resp = HcUtil. \ + get_honeycomb_data(node, "config_bgp_route", path) + if status_code != HTTPCodes.OK: + raise HoneycombError( + "Not possible to get configuration information about the BGP" + " route. Status code: {0}.".format(status_code)) + + return resp + + @staticmethod + def get_all_peer_routes(node, peer_address, ip_version): + """Get all configured routes for the given BGP peer. + + :param node: HOneycomb node. + :param peer_address: IP address of the peer. + :param ip_version: IP protocol version. ipv4 or ipv6 + :type node: dict + :type peer_address: str + :type ip_version: str + :returns: Content of response. + :rtype: bytearray + :raises HoneycombError: If the operation fails. + """ + + if ip_version.lower() == "ipv4": + path = "{0}/tables/bgp-types:ipv4-address-family/" \ + "bgp-types:unicast-subsequent-address-family/" \ + "bgp-inet:ipv4-routes".format(peer_address) + else: + path = "{0}/tables/bgp-types:ipv6-address-family/" \ + "bgp-types:unicast-subsequent-address-family/" \ + "bgp-inet:ipv6-routes".format(peer_address) + status_code, resp = HcUtil. \ + get_honeycomb_data(node, "config_bgp_route", path) + if status_code != HTTPCodes.OK: + raise HoneycombError( + "Not possible to get configuration information about BGP" + " routes. Status code: {0}.".format(status_code)) + + return resp + + @staticmethod + def remove_bgp_route(node, peer_address, route_address, index, ip_version): + """Remove the specified BGP route from configuration. + + :param node: Honeycomb node. + :param peer_address: IP address of the BGP peer. + :param route_address: IP address of the route. + :param index: Index number of the route within specified peer. + :param ip_version: IP protocol version. ipv4 or ipv6 + :type node: dict + :type peer_address: str + :type route_address: str + :type index: int + :type ip_version: str + :returns: Content of response. + :rtype: bytearray + """ + + route_address = route_address.replace("/", "%2F") + + if ip_version.lower() == "ipv4": + path = "{0}/tables/bgp-types:ipv4-address-family/" \ + "bgp-types:unicast-subsequent-address-family/" \ + "bgp-inet:ipv4-routes/ipv4-route/{1}/{2}" \ + .format(peer_address, route_address, index) + else: + path = "{0}/tables/bgp-types:ipv6-address-family/" \ + "bgp-types:unicast-subsequent-address-family/" \ + "bgp-inet:ipv6-routes/ipv6-route/{1}/{2}" \ + .format(peer_address, route_address, index) + + return BGPKeywords._configure_bgp_route(node, path) diff --git a/resources/libraries/python/honeycomb/HoneycombSetup.py b/resources/libraries/python/honeycomb/HoneycombSetup.py index d10a5ccb4e..fba2bd4225 100644 --- a/resources/libraries/python/honeycomb/HoneycombSetup.py +++ b/resources/libraries/python/honeycomb/HoneycombSetup.py @@ -293,7 +293,8 @@ class HoneycombSetup(object): "which java", "java -version", "dpkg --list | grep openjdk", - "ls -la /opt/honeycomb") + "ls -la /opt/honeycomb", + "cat /opt/honeycomb/modules/*module-config") for node in nodes: if node['type'] == NodeType.DUT: @@ -365,27 +366,32 @@ class HoneycombSetup(object): """ disabled_features = { - "NSH": "io.fd.hc2vpp.vppnsh.impl.VppNshModule" + "NSH": ["io.fd.hc2vpp.vppnsh.impl.VppNshModule"], + "BGP": ["io.fd.hc2vpp.bgp.inet.BgpInetModule", + "io.fd.honeycomb.infra.bgp.BgpModule", + "io.fd.honeycomb.infra.bgp.BgpReadersModule", + "io.fd.honeycomb.infra.bgp.BgpWritersModule"] } ssh = SSH() ssh.connect(node) if feature in disabled_features.keys(): - # uncomment by replacing the entire line - find = replace = "{0}".format(disabled_features[feature]) - if disable: - replace = "// {0}".format(find) - - argument = '"/{0}/c\\ {1}"'.format(find, replace) - path = "{0}/modules/*module-config"\ - .format(Const.REMOTE_HC_DIR) - command = "sed -i {0} {1}".format(argument, path) - - (ret_code, _, stderr) = ssh.exec_command_sudo(command) - if ret_code != 0: - raise HoneycombError("Failed to modify configuration on " - "node {0}, {1}".format(node, stderr)) + # for every module, uncomment by replacing the entire line + for item in disabled_features[feature]: + find = replace = "{0}".format(item) + if disable: + replace = "// {0}".format(find) + + argument = '"/{0}/c\\ {1}"'.format(find, replace) + path = "{0}/modules/*module-config"\ + .format(Const.REMOTE_HC_DIR) + command = "sed -i {0} {1}".format(argument, path) + + (ret_code, _, stderr) = ssh.exec_command_sudo(command) + if ret_code != 0: + raise HoneycombError("Failed to modify configuration on " + "node {0}, {1}".format(node, stderr)) else: raise HoneycombError( "Unrecognized feature {0}.".format(feature)) diff --git a/resources/libraries/python/honeycomb/Routing.py b/resources/libraries/python/honeycomb/Routing.py index 495e96e0fd..4c6a3f205b 100644 --- a/resources/libraries/python/honeycomb/Routing.py +++ b/resources/libraries/python/honeycomb/Routing.py @@ -157,7 +157,7 @@ class RoutingKeywords(object): if status_code != HTTPCodes.OK: raise HoneycombError( "Not possible to get operational information about the " - "classify tables. Status code: {0}.".format(status_code)) + "routing tables. Status code: {0}.".format(status_code)) data = RoutingKeywords.clean_routing_oper_data( resp['routing-protocol'][0]['static-routes'] diff --git a/resources/libraries/robot/honeycomb/bgp.robot b/resources/libraries/robot/honeycomb/bgp.robot new file mode 100644 index 0000000000..d4c2d6b0a2 --- /dev/null +++ b/resources/libraries/robot/honeycomb/bgp.robot @@ -0,0 +1,173 @@ +# Copyright (c) 2017 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. + +*** Settings *** +| Library | resources.libraries.python.honeycomb.BGP.BGPKeywords +| Library | resources.libraries.python.honeycomb.HcAPIKwInterfaces.InterfaceKeywords + +*** Keywords *** +| No BGP peers should be configured +| | [Documentation] | Uses Honeycomb API to read BGP configuration and checks +| | ... | if there ary BGP peers conffigured. +| | ... +| | ... | *Arguments:* +| | ... | - node - information about a DUT node. Type: dictionary +| | ... +| | ... | *Example:* +| | ... +| | ... | \| No BGP peers should be configured \| ${nodes['DUT1']} \| +| | ... +| | [Arguments] | ${node} +| | ... +| | ${oper_data}= | Get Full BGP Configuration | ${node} +| | Should be Empty | ${oper_data['bgp-openconfig-extensions:bgp']['neighbors']} + +| Honeycomb adds BGP peer +| | [Documentation] | Uses Honeycomb API to add a BGP peer. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - address - IP address of the peer. Type: string +| | ... | - data - Peer configuration data. Type: dictionary +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Honeycomb adds BGP peer \| ${nodes['DUT1']} \| 192.168.0.1 \ +| | ... | \| ${data} \| +| | ... +| | [Arguments] | ${node} | ${address} | ${data} +| | ... +| | Add BGP Peer | ${node} | ${address} | ${data} + +| BGP Peer From Honeycomb Should be +| | [Documentation] | Uses Honeycomb API to verify BGP peer operational data. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - address - IP address of the peer. Type: string +| | ... | - data - Peer configuration data. Type: dictionary +| | ... +| | ... | *Example:* +| | ... +| | ... | \| BGP Peer From Honeycomb Should be \ +| | ... | \| ${nodes['DUT1']} \| 192.168.0.1 \| ${data} \| +| | ... +| | [Arguments] | ${node} | ${address} | ${data} +| | ... +| | ${oper_data}= | Get BGP Peer | ${node} | ${address} +| | Compare Data Structures | ${oper_data} | ${data} + +| Honeycomb removes BGP peer +| | [Documentation] | Uses Honeycomb API to add a BGP peer. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - address - IP address of the peer. Type: string +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Honeycomb adds BGP peer \| ${nodes['DUT1']} \| 192.168.0.1 \| +| | ... +| | [Arguments] | ${node} | ${address} +| | ... +| | Remove BGP Peer | ${node} | ${address} + +| Honeycomb configures BGP route +| | [Documentation] | Uses Honeycomb API to add a BGP route\ +| | ... | to the specified peer. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - peer_address - IP address of the peer. Type: string +| | ... | - data - Peer configuration data. Type: dictionary +| | ... | - route_address - IP address of the route. Type: string +| | ... | - route_index - Numeric index of the route under the peer.\ +| | ... | Type: integer +| | ... | - ip_version - IP protocol version, ipv4 or ipv6. Type: string +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Honeycomb adds BGP peer \| ${nodes['DUT1']} \| 192.168.0.1 \ +| | ... | \| ${data} \| 192.168.0.2 \| ${0} \| ipv4 \| +| | ... +| | [Arguments] | ${node} | ${peer_address} | ${data} +| | ... | ${route_address} | ${route_index} | ${ip_version} +| | ... +| | Configure BGP Route | ${node} | ${peer_address} | ${data} +| | ... | ${route_address} | ${route_index} | ${ip_version} + +| BGP Route From Honeycomb Should be +| | [Documentation] | Uses Honeycomb API to verify BGP route operational data. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - peer_address - IP address of the peer. Type: string +| | ... | - data - Peer configuration data. Type: dictionary +| | ... | - route_address - IP address of the route. Type: string +| | ... | - route_index - Numeric index of the route under the peer.\ +| | ... | Type: integer +| | ... | - ip_version - IP protocol version, ipv4 or ipv6. Type: string +| | ... +| | ... | *Example:* +| | ... +| | ... | \| BGP Peers From Honeycomb Should Include \ +| | ... | \| ${nodes['DUT1']} \| ${data} \| +| | ... +| | [Arguments] | ${node} | ${peer_address} | ${data} +| | ... | ${route_address} | ${route_index} | ${ip_version} +| | ... +| | ${oper_data}= | Get BGP Route | ${node} | ${peer_address} +| | ... | ${route_address} | ${route_index} | ${ip_version} +| | Compare Data Structures | ${oper_data} | ${data} + +| Honeycomb removes BGP route +| | [Documentation] | Uses Honeycomb API to remove a BGP route. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - peer_address - IP address of the peer. Type: string +| | ... | - route_address - IP address of the route. Type: string +| | ... | - route_index - Numeric index of the route under the peer.\ +| | ... | Type: integer +| | ... | - ip_version - IP protocol version, ipv4 or ipv6. Type: string +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Honeycomb removes BGP route \| ${nodes['DUT1']} \| 192.168.0.1 \ +| | ... | \| 192.168.0.2 \| ${0} \| ipv4 \| +| | ... +| | [Arguments] | ${node} | ${peer_address} | ${route_address} +| | ... | ${route_index} | ${ip_version} +| | ... +| | Remove BGP Route | ${node} | ${peer_address} | ${route_address} +| | ... | ${route_index} | ${ip_version} + +| No BGP routes should be configured +| | [Documentation] | Uses Honeycomb API to verify that no BGP routes\ +| | ... | are configured under the specified peer. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - peer_address - IP address of the peer. Type: string +| | ... | - ip_version - IP protocol version, ipv4 or ipv6. Type: string +| | ... +| | ... | *Example:* +| | ... +| | ... | \| No BGP routes should be configured \| ${nodes['DUT1']} \ +| | ... | \| 192.168.0.1 \| ipv4 \| +| | ... +| | [Arguments] | ${node} | ${peer_address} | ${ip_version} +| | ... +| | ${oper_data}= | Get All Peer Routes +| | ... | ${node} | ${peer_address} | ${ip_version} +| | Should be Empty | ${oper_data['bgp-inet:${ip_version}-routes']} -- cgit 1.2.3-korg