From: Radovan Sajben Date: Thu, 18 Feb 2016 11:59:40 +0000 (+0100) Subject: Carrying LSP State Information in BGP X-Git-Tag: release/beryllium-sr1~18 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=integration%2Ftest.git;a=commitdiff_plain;h=c1e47dcd89e9a06f125ffb4e7debb66386994f79 Carrying LSP State Information in BGP - basic functional test Change-Id: I1866d7d96dc4265c595d709714bc381a2b5ff61b Signed-off-by: Peter Gubka --- diff --git a/csit/suites/bgpcep/bgpuser/ibgp_peer_lsp.robot b/csit/suites/bgpcep/bgpuser/ibgp_peer_lsp.robot new file mode 100644 index 0000000000..298eec2b12 --- /dev/null +++ b/csit/suites/bgpcep/bgpuser/ibgp_peer_lsp.robot @@ -0,0 +1,119 @@ +*** Settings *** +Documentation Basic tests for iBGP peers. +... +... Copyright (c) 2015-2016 Cisco Systems, Inc. and others. All rights reserved. +... +... This program and the accompanying materials are made available under the +... terms of the Eclipse Public License v1.0 which accompanies this distribution, +... and is available at http://www.eclipse.org/legal/epl-v10.html +... +... Test suite performs basic iBGP functional test case for +... carrying LSP State Information in BGP as described in +... http://tools.ietf.org/html/draft-ietf-idr-te-lsp-distribution-03 +... +Suite Setup Setup_Everything +Suite Teardown Teardown_Everything +Test Setup SetupUtils.Setup_Test_With_Logging_And_Without_Fast_Failing +Test Teardown SetupUtils.Teardown_Test_Show_Bugs_If_Test_Failed +Library OperatingSystem +Library RequestsLibrary +Library DateTime +Variables ${CURDIR}/../../../variables/Variables.py +Variables ${CURDIR}/../../../variables/bgpuser/variables.py ${ODL_SYSTEM_PROMPT} +Resource ${CURDIR}/../../../libraries/BGPcliKeywords.robot +Resource ${CURDIR}/../../../libraries/BGPSpeaker.robot +Resource ${CURDIR}/../../../libraries/ConfigViaRestconf.robot +Resource ${CURDIR}/../../../libraries/FailFast.robot +Resource ${CURDIR}/../../../libraries/KarafKeywords.robot +Resource ${CURDIR}/../../../libraries/KillPythonTool.robot +Resource ${CURDIR}/../../../libraries/SetupUtils.robot +Resource ${CURDIR}/../../../libraries/SSHKeywords.robot +Resource ${CURDIR}/../../../libraries/Utils.robot +Resource ${CURDIR}/../../../libraries/WaitForFailure.robot + +*** Variables *** +${BGP_VARIABLES_FOLDER} ${CURDIR}/../../../variables/bgpuser/ +${COUNT} 1 +${HOLDTIME} 180 +${BGP_PEER_LOG_FILE} bgp_peer.log +${BGP_PEER_COMMAND} python play.py --amount ${COUNT} --myip=${TOOLS_SYSTEM_IP} --myport=${BGP_TOOL_PORT} --peerip=${ODL_SYSTEM_IP} --peerport=${ODL_BGP_PORT} --${BGP_PEER_LOG_LEVEL} --logfile ${BGP_PEER_LOG_FILE} --bgpls True +${BGP_PEER_OPTIONS} &>${BGP_PEER_LOG_FILE} +${DEFAULT_RIB_CHECK_PERIOD} 1s +${DEFAULT_RIB_CHECK_TIMEOUT} 10s +${BGP_PEER_LOG_LEVEL} debug +${CONTROLLER_LOG_LEVEL} INFO +${CONTROLLER_BGP_LOG_LEVEL} DEFAULT + +*** Test Cases *** +TC1_Configure_iBGP_Peer + [Tags] critical + [Documentation] Configure BGP peer module with initiate-connection set to false. + ${template_as_string}= BuiltIn.Set_Variable {'NAME': 'example-bgp-peer', 'IP': '${TOOLS_SYSTEM_IP}', 'HOLDTIME': '${HOLDTIME}', 'PEER_PORT': '${BGP_TOOL_PORT}', 'INITIATE': 'false'} + ConfigViaRestconf.Put_Xml_Template_Folder_Config_Via_Restconf ${BGP_VARIABLES_FOLDER}${/}bgp_peer ${template_as_string} + +TC1_Check_Example_Bgp_Rib_Is_Empty + [Documentation] Check RIB for none linkstate-routes + [Tags] critical + SSHLibrary.Switch Connection bgp_peer_console + Check_Example_Bgp_Rib_Does_Not_Contain "bgp-linkstate:linkstate-routes": {.+} + +TC1_Connect_BGP_Peer + [Documentation] Connect BGP peer + [Tags] critical + SSHLibrary.Switch Connection bgp_peer_console + BGPcliKeywords.Start_Console_Tool ${BGP_PEER_COMMAND} ${BGP_PEER_OPTIONS} + BGPcliKeywords.Read_And_Fail_If_Prompt_Is_Seen + +TC1_Check_Example_Bgp_Rib + [Documentation] Check RIB for linkstate-route(s) + [Tags] critical + SSHLibrary.Switch Connection bgp_peer_console + BuiltIn.Wait_Until_Keyword_Succeeds ${DEFAULT_RIB_CHECK_TIMEOUT} ${DEFAULT_RIB_CHECK_PERIOD} Check_Example_Bgp_Rib_Content "bgp-linkstate:linkstate-routes": {.+} + +TC1_Disconnect_BGP_Peer + [Documentation] Stop BGP peer & store logs + [Tags] critical + SSHLibrary.Switch Connection bgp_peer_console + BGPcliKeywords.Stop_Console_Tool + BGPcliKeywords.Store_File_To_Workspace ${BGP_PEER_LOG_FILE} tc1_${BGP_PEER_LOG_FILE} + +*** Keywords *** +Setup_Everything + [Documentation] SSH-login to mininet machine, create HTTP session, + ... prepare directories for responses, put Python tool to mininet machine, setup imported resources. + SetupUtils.Setup_Utils_For_Setup_And_Teardown + SSHLibrary.Set_Default_Configuration prompt=${TOOLS_SYSTEM_PROMPT} + SSHLibrary.Open_Connection ${TOOLS_SYSTEM_IP} alias=bgp_peer_console + Utils.Flexible_Mininet_Login + SSHKeywords.Require_Python + SSHKeywords.Assure_Library_Ipaddr target_dir=. + SSHLibrary.Put_File ${CURDIR}/../../../../tools/fastbgp/play.py + RequestsLibrary.Create_Session operational http://${ODL_SYSTEM_IP}:${RESTCONFPORT}${OPERATIONAL_API} auth=${AUTH} + ConfigViaRestconf.Setup_Config_Via_Restconf + KarafKeywords.Execute_Controller_Karaf_Command_On_Background log:set ${CONTROLLER_LOG_LEVEL} + KarafKeywords.Execute_Controller_Karaf_Command_On_Background log:set ${CONTROLLER_BGP_LOG_LEVEL} org.opendaylight.bgpcep + KarafKeywords.Execute_Controller_Karaf_Command_On_Background log:set ${CONTROLLER_BGP_LOG_LEVEL} org.opendaylight.protocol + +Teardown_Everything + [Documentation] Create and Log the diff between expected and actual responses, make sure Python tool was killed. + ... Tear down imported Resources. + KillPythonTool.Search_And_Kill_Remote_Python 'play\.py' + ConfigViaRestconf.Teardown_Config_Via_Restconf + RequestsLibrary.Delete_All_Sessions + SSHLibrary.Close_All_Connections + +Check_Example_Bgp_Rib_Content + [Arguments] ${pattern} ${error_message}=Expected pattern not found. + [Documentation] Check the example-bgp-rib content for string + ${response}= RequestsLibrary.Get Request operational bgp-rib:bgp-rib/rib/example-bgp-rib + BuiltIn.Log ${response.status_code} + BuiltIn.Log ${response.text} + BuiltIn.Should_Match_Regexp ${response.text} ${pattern} ${error_message} values=False + +Check_Example_Bgp_Rib_Does_Not_Contain + [Arguments] ${pattern} ${error_message}=Unexpected pattern found. + [Documentation] Check the example-bgp-rib does not contain the string + ${response}= RequestsLibrary.Get Request operational bgp-rib:bgp-rib/rib/example-bgp-rib + BuiltIn.Log ${response.status_code} + BuiltIn.Log ${response.text} + BuiltIn.Should_Not_Match_Regexp ${response.text} ${pattern} ${error_message} values=False diff --git a/csit/testplans/bgpcep-userfeatures-beryllium.txt b/csit/testplans/bgpcep-userfeatures-beryllium.txt new file mode 100644 index 0000000000..e60a02e6ec --- /dev/null +++ b/csit/testplans/bgpcep-userfeatures-beryllium.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v1.0 which accompanies this distribution, +# and is available at http://www.eclipse.org/legal/epl-v10.html + +# Place the suites in run order: +integration/test/csit/suites/netconf/ready/netconfready.robot +integration/test/csit/suites/bgpcep/bgpuser/ibgp_peer_lsp.robot diff --git a/tools/fastbgp/play.py b/tools/fastbgp/play.py index 170f8acd32..dfc4f0ef98 100755 --- a/tools/fastbgp/play.py +++ b/tools/fastbgp/play.py @@ -104,7 +104,31 @@ def parse_arguments(): str_help = "Minimum number of updates to reach to include result into csv." parser.add_argument("--threshold", default="1000", type=int, help=str_help) str_help = "RFC 4760 Multiprotocol Extensions for BGP-4 supported" - parser.add_argument("--rfc4760", default="yes", type=str, help=str_help) + parser.add_argument("--rfc4760", default=True, type=bool, help=str_help) + str_help = "Link-State NLRI supported" + parser.add_argument("--bgpls", default=False, type=bool, help=str_help) + str_help = "Link-State NLRI: Identifier" + parser.add_argument("-lsid", default="1", type=int, help=str_help) + str_help = "Link-State NLRI: Tunnel ID" + parser.add_argument("-lstid", default="1", type=int, help=str_help) + str_help = "Link-State NLRI: LSP ID" + parser.add_argument("-lspid", default="1", type=int, help=str_help) + str_help = "Link-State NLRI: IPv4 Tunnel Sender Address" + parser.add_argument("--lstsaddr", default="1.2.3.4", + type=ipaddr.IPv4Address, help=str_help) + str_help = "Link-State NLRI: IPv4 Tunnel End Point Address" + parser.add_argument("--lsteaddr", default="5.6.7.8", + type=ipaddr.IPv4Address, help=str_help) + str_help = "Link-State NLRI: Identifier Step" + parser.add_argument("-lsidstep", default="1", type=int, help=str_help) + str_help = "Link-State NLRI: Tunnel ID Step" + parser.add_argument("-lstidstep", default="2", type=int, help=str_help) + str_help = "Link-State NLRI: LSP ID Step" + parser.add_argument("-lspidstep", default="4", type=int, help=str_help) + str_help = "Link-State NLRI: IPv4 Tunnel Sender Address Step" + parser.add_argument("-lstsaddrstep", default="16", type=int, help=str_help) + str_help = "Link-State NLRI: IPv4 Tunnel End Point Address Step" + parser.add_argument("-lsteaddrstep", default="1", type=int, help=str_help) str_help = "How many play utilities are to be started." parser.add_argument("--multiplicity", default="1", type=int, help=str_help) arguments = parser.parse_args() @@ -300,7 +324,22 @@ class MessageGenerator(object): self.remaining_prefixes_threshold = self.total_prefix_amount - args.prefill self.results_file_name_default = args.results self.performance_threshold_default = args.threshold - self.rfc4760 = args.rfc4760 == "yes" + self.rfc4760 = args.rfc4760 + self.bgpls = args.bgpls + # Default values when BGP-LS Attributes are used + if self.bgpls: + self.prefix_count_to_add_default = 1 + self.prefix_count_to_del_default = 0 + self.ls_nlri_default = {"Identifier": args.lsid, + "TunnelID": args.lstid, + "LSPID": args.lspid, + "IPv4TunnelSenderAddress": args.lstsaddr, + "IPv4TunnelEndPointAddress": args.lsteaddr} + self.lsid_step = args.lsidstep + self.lstid_step = args.lstidstep + self.lspid_step = args.lspidstep + self.lstsaddr_step = args.lstsaddrstep + self.lsteaddr_step = args.lsteaddrstep # Default values used for randomized part s1_slots = ((self.total_prefix_amount - self.remaining_prefixes_threshold - 1) / @@ -319,7 +358,6 @@ class MessageGenerator(object): self.prefix_count_to_add_default + 1) self.randomize_lowest_default = s2_first_index self.randomize_highest_default = s2_last_index - # Initialising counters self.phase1_start_time = 0 self.phase1_stop_time = 0 @@ -501,7 +539,27 @@ class MessageGenerator(object): new_index = index return new_index - # Get list of prefixes + def get_ls_nlri_values(self, index): + """Generates LS-NLRI parameters. + http://tools.ietf.org/html/draft-ietf-idr-te-lsp-distribution-03 + + Arguments: + :param index: index (iteration) + Returns: + :return: dictionary of LS NLRI parameters and values + """ + # generating list of LS NLRI parameters + identifier = self.ls_nlri_default["Identifier"] + index / self.lsid_step + ipv4_tunnel_sender_address = self.ls_nlri_default["IPv4TunnelSenderAddress"] + index / self.lstsaddr_step + tunnel_id = self.ls_nlri_default["TunnelID"] + index / self.lstid_step + lsp_id = self.ls_nlri_default["LSPID"] + index / self.lspid_step + ipv4_tunnel_endpoint_address = self.ls_nlri_default["IPv4TunnelEndPointAddress"] + index / self.lsteaddr_step + ls_nlri_values = {"Identifier": identifier, + "IPv4TunnelSenderAddress": ipv4_tunnel_sender_address, + "TunnelID": tunnel_id, "LSPID": lsp_id, + "IPv4TunnelEndPointAddress": ipv4_tunnel_endpoint_address} + return ls_nlri_values + def get_prefix_list(self, slot_index, slot_size=None, prefix_base=None, prefix_len=None, prefix_count=None, randomize=None): """Generates list of IP address prefixes. @@ -610,20 +668,26 @@ class MessageGenerator(object): logger.debug("Prefixes to be withdrawn in this iteration:") prefix_list_to_del = self.get_prefix_list(slot_index_to_del, prefix_count=prefix_count_to_del) - # generating the mesage - if self.single_update_default: - # Send prefixes to be introduced and withdrawn - # in one UPDATE message - msg_out = self.update_message(wr_prefixes=prefix_list_to_del, - nlri_prefixes=prefix_list_to_add) + # generating the UPDATE mesage with LS-NLRI only + if self.bgpls: + ls_nlri = self.get_ls_nlri_values(self.iteration) + msg_out = self.update_message(wr_prefixes=[], nlri_prefixes=[], + **ls_nlri) else: - # Send prefixes to be introduced and withdrawn - # in separate UPDATE messages (if needed) - msg_out = self.update_message(wr_prefixes=[], - nlri_prefixes=prefix_list_to_add) - if prefix_count_to_del: - msg_out += self.update_message(wr_prefixes=prefix_list_to_del, - nlri_prefixes=[]) + # generating the UPDATE message with prefix lists + if self.single_update_default: + # Send prefixes to be introduced and withdrawn + # in one UPDATE message + msg_out = self.update_message(wr_prefixes=prefix_list_to_del, + nlri_prefixes=prefix_list_to_add) + else: + # Send prefixes to be introduced and withdrawn + # in separate UPDATE messages (if needed) + msg_out = self.update_message(wr_prefixes=[], + nlri_prefixes=prefix_list_to_add) + if prefix_count_to_del: + msg_out += self.update_message(wr_prefixes=prefix_list_to_del, + nlri_prefixes=[]) # updating counters - who knows ... maybe I am last time here ;) if straightforward_scenario: self.phase1_stop_time = time.time() @@ -704,6 +768,19 @@ class MessageGenerator(object): ) optional_parameters_hex += optional_parameter_hex + if self.bgpls: + optional_parameter_hex = ( + "\x02" # Param type ("Capability Ad") + "\x06" # Length (6 bytes) + "\x01" # Capability type (NLRI Unicast), + # see RFC 4760, secton 8 + "\x04" # Capability value length + "\x40\x04" # AFI (BGP-LS) + "\x00" # (reserved) + "\x47" # SAFI (BGP-LS) + ) + optional_parameters_hex += optional_parameter_hex + optional_parameter_hex = ( "\x02" # Param type ("Capability Ad") "\x06" # Length (6 bytes) @@ -775,7 +852,8 @@ class MessageGenerator(object): def update_message(self, wr_prefixes=None, nlri_prefixes=None, wr_prefix_length=None, nlri_prefix_length=None, my_autonomous_system=None, next_hop=None, - originator_id=None, cluster_list_item=None): + originator_id=None, cluster_list_item=None, + end_of_rib=False, **ls_nlri_params): """Generates an UPDATE Message (rfc4271#section-4.3) Arguments: @@ -807,6 +885,8 @@ class MessageGenerator(object): originator_id = self.originator_id_default if cluster_list_item is None: cluster_list_item = self.cluster_list_item_default + ls_nlri = self.ls_nlri_default.copy() + ls_nlri.update(ls_nlri_params) # Marker marker_hex = "\xFF" * 16 @@ -816,12 +896,13 @@ class MessageGenerator(object): type_hex = struct.pack("B", type) # Withdrawn Routes - bytes = ((wr_prefix_length - 1) / 8) + 1 withdrawn_routes_hex = "" - for prefix in wr_prefixes: - withdrawn_route_hex = (struct.pack("B", wr_prefix_length) + - struct.pack(">I", int(prefix))[:bytes]) - withdrawn_routes_hex += withdrawn_route_hex + if not self.bgpls: + bytes = ((wr_prefix_length - 1) / 8) + 1 + for prefix in wr_prefixes: + withdrawn_route_hex = (struct.pack("B", wr_prefix_length) + + struct.pack(">I", int(prefix))[:bytes]) + withdrawn_routes_hex += withdrawn_route_hex # Withdrawn Routes Length withdrawn_routes_length = len(withdrawn_routes_hex) @@ -870,17 +951,40 @@ class MessageGenerator(object): ) # one CLUSTER_LIST item (4 bytes) path_attributes_hex += struct.pack(">I", int(cluster_list_item)) + if self.bgpls and not end_of_rib: + path_attributes_hex += ( + "\x80" # Flags ("Optional, non-transitive") + "\x0e" # Type (MP_REACH_NLRI) + "\x22" # Length (34) + "\x40\x04" # AFI (BGP-LS) + "\x47" # SAFI (BGP-LS) + "\x04" # Next Hop Length (4) + ) + path_attributes_hex += struct.pack(">I", int(next_hop)) + path_attributes_hex += "\x00" # Reserved + path_attributes_hex += ( + "\x00\x05" # LS-NLRI.NLRIType (IPv4 TE LSP NLRI) + "\x00\x15" # LS-NLRI.TotalNLRILength (21) + "\x07" # LS-NLRI.Variable.ProtocolID (RSVP-TE) + ) + path_attributes_hex += struct.pack(">Q", int(ls_nlri["Identifier"])) + path_attributes_hex += struct.pack(">I", int(ls_nlri["IPv4TunnelSenderAddress"])) + path_attributes_hex += struct.pack(">H", int(ls_nlri["TunnelID"])) + path_attributes_hex += struct.pack(">H", int(ls_nlri["LSPID"])) + path_attributes_hex += struct.pack(">I", int(ls_nlri["IPv4TunnelEndPointAddress"])) + # Total Path Attributes Length total_path_attributes_length = len(path_attributes_hex) total_path_attributes_length_hex = struct.pack(">H", total_path_attributes_length) # Network Layer Reachability Information - bytes = ((nlri_prefix_length - 1) / 8) + 1 nlri_hex = "" - for prefix in nlri_prefixes: - nlri_prefix_hex = (struct.pack("B", nlri_prefix_length) + - struct.pack(">I", int(prefix))[:bytes]) - nlri_hex += nlri_prefix_hex + if not self.bgpls: + bytes = ((nlri_prefix_length - 1) / 8) + 1 + for prefix in nlri_prefixes: + nlri_prefix_hex = (struct.pack("B", nlri_prefix_length) + + struct.pack(">I", int(prefix))[:bytes]) + nlri_hex += nlri_prefix_hex # Length (big-endian) length = ( @@ -928,6 +1032,8 @@ class MessageGenerator(object): logger.debug(" Originator id=" + str(originator_id)) if cluster_list_item is not None: logger.debug(" Cluster list=" + str(cluster_list_item)) + if self.bgpls: + logger.debug(" MP_REACH_NLRI: %s", ls_nlri) logger.debug(" Network Layer Reachability Information=" + str(nlri_prefixes) + "/" + str(nlri_prefix_length) + " (0x" + binascii.hexlify(nlri_hex) + ")") @@ -1586,7 +1692,8 @@ class StateTracker(object): self.generator.store_results() logger.info("Finally an END-OF-RIB is sent.") msg_out += self.generator.update_message(wr_prefixes=[], - nlri_prefixes=[]) + nlri_prefixes=[], + end_of_rib=True) self.writer.enqueue_message_for_sending(msg_out) # Attempt for real sending to be done in next iteration. return