basic iBGP peers functional suite 56/31356/25
authorRadovan Sajben <rsajben@cisco.com>
Tue, 15 Dec 2015 14:43:27 +0000 (15:43 +0100)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 14 Jan 2016 18:12:47 +0000 (18:12 +0000)
- two iBGP peers as routing reflector clients (rrc)
- one iBGP rrc-peer and one non-rrc iBGP peer
- two iBGP peers (non-rrc)

Change-Id: I7c70e038dbe148dcecbee511df0f8da4ba1189f7
Signed-off-by: Radovan Sajben <rsajben@cisco.com>
csit/suites/bgpcep/bgpuser/ibgp_peers_basic.robot [new file with mode: 0644]
csit/variables/bgpuser/ibgp_peers/config.uri [new file with mode: 0644]
csit/variables/bgpuser/ibgp_peers/data.xml [new file with mode: 0644]
tools/fastbgp/play.py

diff --git a/csit/suites/bgpcep/bgpuser/ibgp_peers_basic.robot b/csit/suites/bgpcep/bgpuser/ibgp_peers_basic.robot
new file mode 100644 (file)
index 0000000..2e58c2e
--- /dev/null
@@ -0,0 +1,396 @@
+*** 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 cases for
+...               BGP peers in different roles (iBGP, iBGP RR-client):
+...
+...               Test Case 1: Two iBGP RR-client peers introduce prefixes
+...               Expected result: controller forwards updates towards both peers
+...
+...               Test Case 2: Two iBGP peers: one RR client and one non-client introduces prefixes
+...               Expected result: controller forwards updates towards both peers
+...
+...               Test Case 3: Two iBGP RR non-client peers introduce prefixes
+...               Expected result: controller does not forward any update towards peers
+...
+...               For polices see: https://wiki.opendaylight.org/view/BGP_LS_PCEP:BGP
+...
+...               Covered bugs:
+...               Bug 4791 - BGPSessionImpl: Failed to send message Update logged even all UPDATE mesages received by iBGP peer
+...               Bug 4819 - No routes advertised to one of newly configured iBGP RR-client peer
+Suite Setup       Setup_Everything
+Suite Teardown    Teardown_Everything
+Test Setup        SetupUtils.Setup_Test_With_Logging_And_Without_Fast_Failing
+Test Teardown     FailFast.Start_Failing_Fast_If_This_Failed
+Library           OperatingSystem
+Library           RequestsLibrary
+Library           DateTime
+Library           ${CURDIR}/../../../libraries/HsfJson/hsf_json.py
+Variables         ${CURDIR}/../../../variables/Variables.py
+Variables         ${CURDIR}/../../../variables/bgpuser/variables.py    ${ODL_SYSTEM_PROMPT}
+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/
+${HOLDTIME}       180
+${BGP_PEER_LOG_LEVEL}    debug
+${CONTROLLER_LOG_LEVEL}    INFO
+${CONTROLLER_BGP_LOG_LEVEL}    DEFAULT
+${BGP_PEER1_IP}    127.0.0.1
+${BGP_PEER2_IP}    127.0.0.2
+${BGP_PEER1_FIRST_PREFIX_IP}    8.1.0.0
+${BGP_PEER2_FIRST_PREFIX_IP}    8.2.0.0
+${PREFIX_LEN}     28
+${BGP_PEER1_PREFIX_LEN}    ${PREFIX_LEN}
+${BGP_PEER2_PREFIX_LEN}    ${PREFIX_LEN}
+${PREFIX_COUNT}    3
+${BGP_PEER1_PREFIX_COUNT}    ${PREFIX_COUNT}
+${BGP_PEER2_PREFIX_COUNT}    ${PREFIX_COUNT}
+${BGP_PEER1_LOG_FILE}    bgp_peer1.log
+${BGP_PEER2_LOG_FILE}    bgp_peer2.log
+${BGP_PEER1_COMMAND}    python play.py --firstprefix ${BGP_PEER1_FIRST_PREFIX_IP} --prefixlen ${BGP_PEER1_PREFIX_LEN} --amount ${BGP_PEER1_PREFIX_COUNT} --myip=${BGP_PEER1_IP} --myport=${BGP_TOOL_PORT} --peerip=${ODL_SYSTEM_IP} --peerport=${ODL_BGP_PORT} --${BGP_PEER_LOG_LEVEL} --logfile ${BGP_PEER1_LOG_FILE}
+${BGP_PEER2_COMMAND}    python play.py --firstprefix ${BGP_PEER2_FIRST_PREFIX_IP} --prefixlen ${BGP_PEER2_PREFIX_LEN} --amount ${BGP_PEER2_PREFIX_COUNT} --myip=${BGP_PEER2_IP} --myport=${BGP_TOOL_PORT} --peerip=${ODL_SYSTEM_IP} --peerport=${ODL_BGP_PORT} --${BGP_PEER_LOG_LEVEL} --logfile ${BGP_PEER2_LOG_FILE}
+${BGP_PEER1_OPTIONS}    &>${BGP_PEER1_LOG_FILE}
+${BGP_PEER2_OPTIONS}    &>${BGP_PEER2_LOG_FILE}
+${DEFAULT_LOG_CHECK_TIMEOUT}    20s
+${DEFAULT_LOG_CHECK_PERIOD}    1s
+${DEFAULT_TOPOLOGY_CHECK_TIMEOUT}    10s
+${DEFAULT_TOPOLOGY_CHECK_PERIOD}    1s
+
+*** Test Cases ***
+TC1_Configure_Two_iBGP_Route_Reflector_Client_Peers
+    [Documentation]    Configure two iBGP peers as routing reflector clients.
+    [Tags]    critical
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer1', 'IP': '${BGP_PEER1_IP}', 'HOLDTIME': '${HOLDTIME}', 'PEER_PORT': '${BGP_TOOL_PORT}','PEER_ROLE': 'rr-client', 'INITIATE': 'false'}
+    ConfigViaRestconf.Put_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer2', 'IP': '${BGP_PEER2_IP}', 'HOLDTIME': '${HOLDTIME}', 'PEER_PORT': '${BGP_TOOL_PORT}','PEER_ROLE': 'rr-client', 'INITIATE': 'false'}
+    ConfigViaRestconf.Put_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer1'}
+    ${result}=    ConfigViaRestconf.Get_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    BuiltIn.Log    ${result}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer2'}
+    ${result}=    ConfigViaRestconf.Get_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    BuiltIn.Log    ${result}
+
+TC1_Connect_BGP_Peer1
+    [Documentation]    Connect BGP peer
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer1_console
+    Start_Console_Tool    ${BGP_PEER1_COMMAND}    ${BGP_PEER1_OPTIONS}
+    Read_And_Fail_If_Prompt_Is_Seen
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_TOPOLOGY_CHECK_TIMEOUT}    ${DEFAULT_TOPOLOGY_CHECK_PERIOD}    Check_Example_IPv4_Topology_Content    {"prefix":"${BGP_PEER1_FIRST_PREFIX_IP}/${PREFIX_LEN}"}
+
+TC1_Connect_BGP_Peer2
+    [Documentation]    Connect BGP peer
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    Start_Console_Tool    ${BGP_PEER2_COMMAND}    ${BGP_PEER2_OPTIONS}
+    Read_And_Fail_If_Prompt_Is_Seen
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_TOPOLOGY_CHECK_TIMEOUT}    ${DEFAULT_TOPOLOGY_CHECK_PERIOD}    Check_Example_IPv4_Topology_Content    {"prefix":"${BGP_PEER2_FIRST_PREFIX_IP}/${PREFIX_LEN}"}
+
+TC1_BGP_Peer1_Check_Log_For_Introduced_Prefixes
+    [Documentation]    Check incomming updates for new routes
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer1_console
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_LOG_CHECK_TIMEOUT}    ${DEFAULT_LOG_CHECK_PERIOD}    Check_File_For_Word_Count    ${BGP_PEER1_LOG_FILE}    nlri_prefix_received:    ${BGP_PEER2_PREFIX_COUNT}
+    Check_File_For_Word_Count    ${BGP_PEER1_LOG_FILE}    nlri_prefix_received: ${BGP_PEER2_FIRST_PREFIX_IP}/${BGP_PEER2_PREFIX_LEN}    1
+    Check_File_For_Word_Count    ${BGP_PEER1_LOG_FILE}    withdrawn_prefix_received:    0
+    [Teardown]    Report_Failure_Due_To_Bug    4819
+
+TC1_BGP_Peer2_Check_Log_For_Introduced_Prefixes
+    [Documentation]    Check incomming updates for new routes
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_LOG_CHECK_TIMEOUT}    ${DEFAULT_LOG_CHECK_PERIOD}    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    nlri_prefix_received:    ${BGP_PEER1_PREFIX_COUNT}
+    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    nlri_prefix_received: ${BGP_PEER1_FIRST_PREFIX_IP}/${BGP_PEER1_PREFIX_LEN}    1
+    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    withdrawn_prefix_received:    0
+    [Teardown]    Report_Failure_Due_To_Bug    4819
+
+TC1_Disconnect_BGP_Peer1
+    [Documentation]    Stop BGP peer & store logs
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer1_console
+    Stop_Console_Tool
+    Store_File_To_Workspace    ${BGP_PEER1_LOG_FILE}    tc1_${BGP_PEER1_LOG_FILE}
+
+TC1_BGP_Peer2_Check_Log_For_Withdrawn_Prefixes
+    [Documentation]    Check incomming updates for withdrawn routes
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_LOG_CHECK_TIMEOUT}    ${DEFAULT_LOG_CHECK_PERIOD}    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    withdrawn_prefix_received:    ${BGP_PEER1_PREFIX_COUNT}
+    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    withdrawn_prefix_received: ${BGP_PEER1_FIRST_PREFIX_IP}/${BGP_PEER1_PREFIX_LEN}    1
+    [Teardown]    Report_Failure_Due_To_Bug    4819
+
+TC1_Disconnect_BGP_Peer2
+    [Documentation]    Stop BGP peer & store logs
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    Stop_Console_Tool
+    Store_File_To_Workspace    ${BGP_PEER2_LOG_FILE}    tc1_${BGP_PEER2_LOG_FILE}
+
+TC_1_Check_for_Empty_IPv4_Topology
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_TOPOLOGY_CHECK_TIMEOUT}    ${DEFAULT_TOPOLOGY_CHECK_PERIOD}    Check_Example_IPv4_Topology_Does_Not_Contain    prefix
+
+TC1_Delete_BGP_Peers_Configuration
+    [Documentation]    Delete all previously configured BGP peers.
+    [Tags]    critical
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer1'}
+    ConfigViaRestconf.Delete_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer2'}
+    ConfigViaRestconf.Delete_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+
+TC2_Configure_One_iBGP_Route_Reflector_Client_And_One_iBGP_Non_Client
+    [Documentation]    Configure iBGP peers: 1st one as RR client, 2nd one as RR non-client.
+    [Tags]    critical
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer1', 'IP': '${BGP_PEER1_IP}', 'HOLDTIME': '${HOLDTIME}', 'PEER_PORT': '${BGP_TOOL_PORT}','PEER_ROLE': 'rr-client', 'INITIATE': 'false'}
+    ConfigViaRestconf.Put_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer2', 'IP': '${BGP_PEER2_IP}', 'HOLDTIME': '${HOLDTIME}', 'PEER_PORT': '${BGP_TOOL_PORT}','PEER_ROLE': 'ibgp', 'INITIATE': 'false'}
+    ConfigViaRestconf.Put_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer1'}
+    ${result}=    ConfigViaRestconf.Get_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    BuiltIn.Log    ${result}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer2'}
+    ${result}=    ConfigViaRestconf.Get_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    BuiltIn.Log    ${result}
+
+TC2_Connect_BGP_Peer1
+    [Documentation]    Connect BGP peer
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer1_console
+    Start_Console_Tool    ${BGP_PEER1_COMMAND}    ${BGP_PEER1_OPTIONS}
+    Read_And_Fail_If_Prompt_Is_Seen
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_TOPOLOGY_CHECK_TIMEOUT}    ${DEFAULT_TOPOLOGY_CHECK_PERIOD}    Check_Example_IPv4_Topology_Content    {"prefix":"${BGP_PEER1_FIRST_PREFIX_IP}/${PREFIX_LEN}"}
+
+TC2_Connect_BGP_Peer2
+    [Documentation]    Connect BGP peer
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    Start_Console_Tool    ${BGP_PEER2_COMMAND}    ${BGP_PEER2_OPTIONS}
+    Read_And_Fail_If_Prompt_Is_Seen
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_TOPOLOGY_CHECK_TIMEOUT}    ${DEFAULT_TOPOLOGY_CHECK_PERIOD}    Check_Example_IPv4_Topology_Content    {"prefix":"${BGP_PEER2_FIRST_PREFIX_IP}/${PREFIX_LEN}"}
+
+TC2_BGP_Peer1_Check_Log_For_Introduced_Prefixes
+    [Documentation]    Check incomming updates for new routes
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer1_console
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_LOG_CHECK_TIMEOUT}    ${DEFAULT_LOG_CHECK_PERIOD}    Check_File_For_Word_Count    ${BGP_PEER1_LOG_FILE}    nlri_prefix_received:    ${BGP_PEER2_PREFIX_COUNT}
+    Check_File_For_Word_Count    ${BGP_PEER1_LOG_FILE}    nlri_prefix_received: ${BGP_PEER2_FIRST_PREFIX_IP}/${BGP_PEER2_PREFIX_LEN}    1
+    Check_File_For_Word_Count    ${BGP_PEER1_LOG_FILE}    withdrawn_prefix_received:    0
+
+TC2_BGP_Peer2_Check_Log_For_Introduced_Prefixes
+    [Documentation]    Check incomming updates for new routes
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_LOG_CHECK_TIMEOUT}    ${DEFAULT_LOG_CHECK_PERIOD}    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    nlri_prefix_received:    ${BGP_PEER1_PREFIX_COUNT}
+    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    nlri_prefix_received: ${BGP_PEER1_FIRST_PREFIX_IP}/${BGP_PEER1_PREFIX_LEN}    1
+    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    withdrawn_prefix_received:    0
+    [Teardown]    Report_Failure_Due_To_Bug    4791
+
+TC2_Disconnect_BGP_Peer1
+    [Documentation]    Stop BGP peer & store logs
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer1_console
+    Stop_Console_Tool
+    Store_File_To_Workspace    ${BGP_PEER1_LOG_FILE}    tc2_${BGP_PEER1_LOG_FILE}
+
+TC2_BGP_Peer2_Check_Log_For_Withdrawn_Prefixes
+    [Documentation]    Check incomming updates for withdrawn routes
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_LOG_CHECK_TIMEOUT}    ${DEFAULT_LOG_CHECK_PERIOD}    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    withdrawn_prefix_received:    ${BGP_PEER1_PREFIX_COUNT}
+    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    withdrawn_prefix_received: ${BGP_PEER1_FIRST_PREFIX_IP}/${BGP_PEER1_PREFIX_LEN}    1
+
+TC2_Disconnect_BGP_Peer2
+    [Documentation]    Stop BGP peer & store logs
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    Stop_Console_Tool
+    Store_File_To_Workspace    ${BGP_PEER2_LOG_FILE}    tc2_${BGP_PEER2_LOG_FILE}
+
+TC_2_Check_for_Empty_IPv4_Topology
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_TOPOLOGY_CHECK_TIMEOUT}    ${DEFAULT_TOPOLOGY_CHECK_PERIOD}    Check_Example_IPv4_Topology_Does_Not_Contain    prefix
+
+TC2_Delete_BGP_Peers_Configuration
+    [Documentation]    Delete all previously configured BGP peers.
+    [Tags]    critical
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer1'}
+    ConfigViaRestconf.Delete_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer2'}
+    ConfigViaRestconf.Delete_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+
+TC3_Configure_Two_iBGP_Non_Client_Peers
+    [Documentation]    Configure iBGP peers: 1st one as RR client, 2nd one as RR non-client.
+    [Tags]    critical
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer1', 'IP': '${BGP_PEER1_IP}', 'HOLDTIME': '${HOLDTIME}', 'PEER_PORT': '${BGP_TOOL_PORT}','PEER_ROLE': 'ibgp', 'INITIATE': 'false'}
+    ConfigViaRestconf.Put_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer2', 'IP': '${BGP_PEER2_IP}', 'HOLDTIME': '${HOLDTIME}', 'PEER_PORT': '${BGP_TOOL_PORT}','PEER_ROLE': 'ibgp', 'INITIATE': 'false'}
+    ConfigViaRestconf.Put_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer1'}
+    ${result}=    ConfigViaRestconf.Get_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    BuiltIn.Log    ${result}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer2'}
+    ${result}=    ConfigViaRestconf.Get_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    BuiltIn.Log    ${result}
+
+TC3_Connect_BGP_Peer1
+    [Documentation]    Connect BGP peer
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer1_console
+    Start_Console_Tool    ${BGP_PEER1_COMMAND}    ${BGP_PEER1_OPTIONS}
+    Read_And_Fail_If_Prompt_Is_Seen
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_TOPOLOGY_CHECK_TIMEOUT}    ${DEFAULT_TOPOLOGY_CHECK_PERIOD}    Check_Example_IPv4_Topology_Content    {"prefix":"${BGP_PEER1_FIRST_PREFIX_IP}/${PREFIX_LEN}"}
+
+TC3_Connect_BGP_Peer2
+    [Documentation]    Connect BGP peer
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    Start_Console_Tool    ${BGP_PEER2_COMMAND}    ${BGP_PEER2_OPTIONS}
+    Read_And_Fail_If_Prompt_Is_Seen
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_TOPOLOGY_CHECK_TIMEOUT}    ${DEFAULT_TOPOLOGY_CHECK_PERIOD}    Check_Example_IPv4_Topology_Content    {"prefix":"${BGP_PEER2_FIRST_PREFIX_IP}/${PREFIX_LEN}"}
+
+TC3_BGP_Peer1_Check_Log_For_No_Updates
+    [Documentation]    Check for no updates received by iBGP peer No. 1
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer1_console
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_LOG_CHECK_TIMEOUT}    ${DEFAULT_LOG_CHECK_PERIOD}    Check_File_For_Word_Count    ${BGP_PEER1_LOG_FILE}    total_received_update_message_counter: 0    2
+
+TC3_Disconnect_BGP_Peer1
+    [Documentation]    Stop BGP peer & store logs
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer1_console
+    Stop_Console_Tool
+    Store_File_To_Workspace    ${BGP_PEER1_LOG_FILE}    tc3_${BGP_PEER1_LOG_FILE}
+
+TC3_BGP_Peer2_Check_Log_For_No_Updates
+    [Documentation]    Consequent check for no updates received by iBGP peer No. 2
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    ${log_check_timeout}=    DateTime.Convert_Time    ${DEFAULT_LOG_CHECK_TIMEOUT}    result_format=number
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${log_check_timeout*2}    ${DEFAULT_LOG_CHECK_PERIOD}    Check_File_For_Word_Count    ${BGP_PEER2_LOG_FILE}    total_received_update_message_counter: 0    4
+
+TC3_Disconnect_BGP_Peer2
+    [Documentation]    Stop BGP peer & store logs
+    [Tags]    critical
+    SSHLibrary.Switch Connection    bgp_peer2_console
+    Stop_Console_Tool
+    Store_File_To_Workspace    ${BGP_PEER2_LOG_FILE}    tc3_${BGP_PEER2_LOG_FILE}
+
+TC_3_Check_for_Empty_IPv4_Topology
+    BuiltIn.Wait_Until_Keyword_Succeeds    ${DEFAULT_TOPOLOGY_CHECK_TIMEOUT}    ${DEFAULT_TOPOLOGY_CHECK_PERIOD}    Check_Example_IPv4_Topology_Does_Not_Contain    prefix
+
+TC3_Delete_BGP_Peers_Configuration
+    [Documentation]    Delete all previously configured BGP peers.
+    [Tags]    critical
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer1'}
+    ConfigViaRestconf.Delete_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+    ${template_as_string}=    BuiltIn.Set_Variable    {'NAME': 'example-bgp-peer2'}
+    ConfigViaRestconf.Delete_Xml_Template_Folder_Config_Via_Restconf    ${BGP_VARIABLES_FOLDER}${/}ibgp_peers    ${template_as_string}
+
+*** 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.
+    # TODO: Choose keywords used by more than one test suite to be placed in a common place.
+    SetupUtils.Setup_Utils_For_Setup_And_Teardown
+    SSHLibrary.Set_Default_Configuration    prompt=${ODL_SYSTEM_PROMPT}
+    SSHLibrary.Open_Connection    ${ODL_SYSTEM_IP}    alias=bgp_peer1_console
+    Utils.Flexible_Controller_Login
+    SSHLibrary.Open_Connection    ${ODL_SYSTEM_IP}    alias=bgp_peer2_console
+    Utils.Flexible_Controller_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_TOPO_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
+
+Start_Console_Tool
+    [Arguments]    ${command}    ${tool_opt}
+    [Documentation]    Start the tool ${command} ${tool_opt}
+    BuiltIn.Log    ${command}
+    ${output}=    SSHLibrary.Write    ${command} ${tool_opt}
+    BuiltIn.Log    ${output}
+
+Wait_Until_Console_Tool_Finish
+    [Arguments]    ${timeout}
+    [Documentation]    Wait ${timeout} for the tool exit.
+    BuiltIn.Wait Until Keyword Succeeds    ${timeout}    1s    SSHLibrary.Read Until Prompt
+
+Stop_Console_Tool
+    [Documentation]    Stop the tool if still running.
+    Utils.Write_Bare_Ctrl_C
+    BuiltIn.Wait Until Keyword Succeeds    10s    1s    SSHLibrary.Read Until Prompt
+
+Check_Example_IPv4_Topology_Content
+    [Arguments]    ${string_to_check}=${EMPTY}
+    [Documentation]    Check the example-ipv4-topology content for string
+    ${response}=    RequestsLibrary.Get Request    operational    topology/example-ipv4-topology
+    BuiltIn.Log    ${response.status_code}
+    BuiltIn.Log    ${response.text}
+    BuiltIn.Should_Contain    ${response.text}    ${string_to_check}
+
+Check_Example_IPv4_Topology_Does_Not_Contain
+    [Arguments]    ${string_to_check}
+    [Documentation]    Check the example-ipv4-topology does not contain the string
+    ${response}=    RequestsLibrary.Get Request    operational    topology/example-ipv4-topology
+    BuiltIn.Log    ${response.status_code}
+    BuiltIn.Log    ${response.text}
+    BuiltIn.Should_Not_Contain    ${response.text}    ${string_to_check}
+
+Read_And_Fail_If_Prompt_Is_Seen
+    [Documentation]    Try to read SSH to see prompt, but expect to see no prompt within SSHLibrary's timeout.
+    ${passed}=    BuiltIn.Run_Keyword_And_Return_Status    BuiltIn.Run_Keyword_And_Expect_Error    No match found for '${ODL_SYSTEM_PROMPT}' in *.    Read_Text_Before_Prompt
+    BuiltIn.Return_From_Keyword_If    ${passed}
+    BGPSpeaker.Dump_BGP_Speaker_Logs
+    Builtin.Fail    The prompt was seen but it was not expected yet
+
+Read_Text_Before_Prompt
+    [Documentation]    Log text gathered by SSHLibrary.Read_Until_Prompt.
+    ...    This needs to be a separate keyword just because how Read_And_Fail_If_Prompt_Is_Seen is implemented.
+    ${text}=    SSHLibrary.Read_Until_Prompt
+    BuiltIn.Log    ${text}
+
+Store_File_To_Workspace
+    [Arguments]    ${source_file_name}    ${target_file_name}
+    [Documentation]    Store the ${source_file_name} to the workspace as ${target_file_name}.
+    ${output_log}=    SSHLibrary.Execute_Command    cat ${source_file_name}
+    BuiltIn.Log    ${output_log}
+    Create File    ${target_file_name}    ${output_log}
+
+Check_File_For_Word_Count
+    [Arguments]    ${file_name}    ${word}    ${expected_count}
+    [Documentation]    Count ${word} in ${file_name}. Expect ${expected_count} occurence(s)
+    ${output_log}=    SSHLibrary.Execute_Command    grep -o '${word}' ${file_name} | wc -l
+    BuiltIn.Log    ${output_log}
+    BuiltIn.Should_Be_Equal_As_Strings    ${output_log}    ${expected_count}
+
+Check_File_For_Occurence
+    [Arguments]    ${file_name}    ${keyword}    ${value}=''
+    [Documentation]    Check file for ${keyword} or ${keyword} ${value} pair and returns number of occurences
+    ${output_log}=    SSHLibrary.Execute_Command    grep '${keyword}' ${file_name} | grep -c ${value}
+    ${count}=    Convert To Integer    ${output_log}
+    [Return]    ${count}
diff --git a/csit/variables/bgpuser/ibgp_peers/config.uri b/csit/variables/bgpuser/ibgp_peers/config.uri
new file mode 100644 (file)
index 0000000..383950a
--- /dev/null
@@ -0,0 +1 @@
+config:modules/module/odl-bgp-rib-impl-cfg:bgp-peer/$NAME
diff --git a/csit/variables/bgpuser/ibgp_peers/data.xml b/csit/variables/bgpuser/ibgp_peers/data.xml
new file mode 100644 (file)
index 0000000..a7e01bc
--- /dev/null
@@ -0,0 +1,29 @@
+<module xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:bgp-peer</type>
+    <name>$NAME</name>
+    <host xmlns="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">$IP</host>
+    <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">$PEER_PORT</port>
+    <peer-role xmlns="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">$PEER_ROLE</peer-role>
+    <holdtimer xmlns="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">$HOLDTIME</holdtimer>
+    <initiate-connection xmlns="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">$INITIATE</initiate-connection>
+    <rib xmlns="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">
+        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:rib-instance</type>
+        <name>example-bgp-rib</name>
+    </rib>
+    <peer-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">
+        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:bgp-peer-registry</type>
+        <name>global-bgp-peer-registry</name>
+    </peer-registry>
+    <advertized-table xmlns="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">
+        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:bgp-table-type</type>
+        <name>ipv4-unicast</name>
+    </advertized-table>
+    <advertized-table xmlns="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">
+        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:bgp-table-type</type>
+        <name>ipv6-unicast</name>
+    </advertized-table>
+    <advertized-table xmlns="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">
+        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:bgp-table-type</type>
+        <name>linkstate</name>
+    </advertized-table>
+</module>
index 1d96f184b9fb7c747a1077ec96c87bdc7acd5ea0..b37b5182ce5491c183c688a637a6e685329a0e3c 100755 (executable)
@@ -70,6 +70,12 @@ def parse_arguments():
     str_help = "The IP of the next hop to be placed into the update messages."
     parser.add_argument("--nexthop", default="192.0.2.1",
                         type=ipaddr.IPv4Address, dest="nexthop", help=str_help)
+    str_help = "Identifier of the route originator."
+    parser.add_argument("--originator", default=None,
+                        type=ipaddr.IPv4Address, dest="originator", help=str_help)
+    str_help = "Cluster list item identifier."
+    parser.add_argument("--cluster", default=None,
+                        type=ipaddr.IPv4Address, dest="cluster", help=str_help)
     str_help = ("Numeric IP Address to try to connect to." +
                 "Currently no effect in listening mode.")
     parser.add_argument("--peerip", default="127.0.0.2",
@@ -274,6 +280,8 @@ class MessageGenerator(object):
         self.hold_time_default = args.holdtime  # Local hold time.
         self.bgp_identifier_default = int(args.myip)
         self.next_hop_default = args.nexthop
+        self.originator_id_default = args.originator
+        self.cluster_list_item_default = args.cluster
         self.single_update_default = args.updates == "single"
         self.randomize_updates_default = args.updates == "random"
         self.prefix_count_to_add_default = args.insert
@@ -338,6 +346,8 @@ class MessageGenerator(object):
         logger.info("  My Hold Time: " + str(self.hold_time_default))
         logger.info("  My BGP Identifier: " + str(self.bgp_identifier_default))
         logger.info("  Next Hop: " + str(self.next_hop_default))
+        logger.info("  Originator ID: " + str(self.originator_id_default))
+        logger.info("  Cluster list: " + str(self.cluster_list_item_default))
         logger.info("  Prefix count to be inserted at once: " +
                     str(self.prefix_count_to_add_default))
         logger.info("  Prefix count to be withdrawn at once: " +
@@ -375,6 +385,7 @@ class MessageGenerator(object):
             :return: n/a
         """
         # default values handling
+        # TODO optimize default values handling (use e.g. dicionary.update() approach)
         if file_name is None:
             file_name = self.results_file_name_default
         if threshold is None:
@@ -470,6 +481,7 @@ class MessageGenerator(object):
             Created just as a fame for future generator enhancement.
         """
         # default values handling
+        # TODO optimize default values handling (use e.g. dicionary.update() approach)
         if lowest is None:
             lowest = self.randomize_lowest_default
         if highest is None:
@@ -503,6 +515,7 @@ class MessageGenerator(object):
             :return: list of generated IP address prefixes
         """
         # default values handling
+        # TODO optimize default values handling (use e.g. dicionary.update() approach)
         if slot_size is None:
             slot_size = self.slot_size_default
         if prefix_base is None:
@@ -546,6 +559,7 @@ class MessageGenerator(object):
             Updates global counters.
         """
         # default values handling
+        # TODO optimize default values handling (use e.g. dicionary.update() approach)
         if prefix_count_to_add is None:
             prefix_count_to_add = self.prefix_count_to_add_default
         if prefix_count_to_del is None:
@@ -634,7 +648,8 @@ class MessageGenerator(object):
             :return: encoded OPEN message in HEX
         """
 
-        # Default values handling
+        # default values handling
+        # TODO optimize default values handling (use e.g. dicionary.update() approach)
         if version is None:
             version = self.version_default
         if my_autonomous_system is None:
@@ -754,7 +769,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):
+                       my_autonomous_system=None, next_hop=None,
+                       originator_id=None, cluster_list_item=None):
         """Generates an UPDATE Message (rfc4271#section-4.3)
 
         Arguments:
@@ -768,7 +784,8 @@ class MessageGenerator(object):
             :return: encoded UPDATE message in HEX
         """
 
-        # Default values handling
+        # default values handling
+        # TODO optimize default values handling (use e.g. dicionary.update() approach)
         if wr_prefixes is None:
             wr_prefixes = self.wr_prefixes_default
         if nlri_prefixes is None:
@@ -781,6 +798,10 @@ class MessageGenerator(object):
             my_autonomous_system = self.my_autonomous_system_default
         if next_hop is None:
             next_hop = self.next_hop_default
+        if originator_id is None:
+            originator_id = self.originator_id_default
+        if cluster_list_item is None:
+            cluster_list_item = self.cluster_list_item_default
 
         # Marker
         marker_hex = "\xFF" * 16
@@ -803,12 +824,15 @@ class MessageGenerator(object):
 
         # TODO: to replace hardcoded string by encoding?
         # Path Attributes
+        path_attributes_hex = ""
         if nlri_prefixes != []:
-            path_attributes_hex = (
+            path_attributes_hex += (
                 "\x40"  # Flags ("Well-Known")
                 "\x01"  # Type (ORIGIN)
                 "\x01"  # Length (1)
                 "\x00"  # Origin: IGP
+            )
+            path_attributes_hex += (
                 "\x40"  # Flags ("Well-Known")
                 "\x02"  # Type (AS_PATH)
                 "\x06"  # Length (6)
@@ -826,8 +850,22 @@ class MessageGenerator(object):
             path_attributes_hex += (
                 next_hop_hex  # IP address of the next hop (4 bytes)
             )
-        else:
-            path_attributes_hex = ""
+            if originator_id is not None:
+                path_attributes_hex += (
+                    "\x80"  # Flags ("Optional, non-transitive")
+                    "\x09"  # Type (ORIGINATOR_ID)
+                    "\x04"  # Length (4)
+                            # ORIGINATOR_ID (4 bytes)
+                    + struct.pack(">I", int(originator_id))
+                )
+            if cluster_list_item is not None:
+                path_attributes_hex += (
+                    "\x80"  # Flags ("Optional, non-transitive")
+                    "\x09"  # Type (CLUSTER_LIST)
+                    "\x04"  # Length (4)
+                            # one CLUSTER_LIST item (4 bytes)
+                    + struct.pack(">I", int(cluster_list_item))
+                )
 
         # Total Path Attributes Length
         total_path_attributes_length = len(path_attributes_hex)
@@ -874,12 +912,19 @@ class MessageGenerator(object):
             logger.debug("  Withdrawn_Routes=" + str(wr_prefixes) + "/" +
                          str(wr_prefix_length) + " (0x" +
                          binascii.hexlify(withdrawn_routes_hex) + ")")
-            logger.debug("  Total Path Attributes Length=" +
-                         str(total_path_attributes_length) + " (0x" +
-                         binascii.hexlify(total_path_attributes_length_hex) +
-                         ")")
-            logger.debug("  Path Attributes=" + "(0x" +
-                         binascii.hexlify(path_attributes_hex) + ")")
+            if total_path_attributes_length:
+                logger.debug("  Total Path Attributes Length=" +
+                             str(total_path_attributes_length) + " (0x" +
+                             binascii.hexlify(total_path_attributes_length_hex) + ")")
+                logger.debug("  Path Attributes=" + "(0x" +
+                             binascii.hexlify(path_attributes_hex) + ")")
+                logger.debug("    Origin=IGP")
+                logger.debug("    AS path=" + str(my_autonomous_system))
+                logger.debug("    Next hop=" + str(next_hop))
+                if originator_id is not None:
+                    logger.debug("    Originator id=" + str(originator_id))
+                if cluster_list_item is not None:
+                    logger.debug("    Cluster list=" + str(cluster_list_item))
             logger.debug("  Network Layer Reachability Information=" +
                          str(nlri_prefixes) + "/" + str(nlri_prefix_length) +
                          " (0x" + binascii.hexlify(nlri_hex) + ")")
@@ -1187,56 +1232,65 @@ class ReadTracker(object):
                 hex_to_decode = hex_to_decode[3 + attr_length:]
 
             if attr_type_code == 1:
-                logger.debug("Attribute type = 1 (ORIGIN, flags:0x%s)",
+                logger.debug("Attribute type=1 (ORIGIN, flags:0x%s)",
                              binascii.b2a_hex(attr_flags_hex))
-                logger.debug("Attribute value = 0x%s", binascii.b2a_hex(attr_value_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
             elif attr_type_code == 2:
-                logger.debug("Attribute type = 2 (AS_PATH, flags:0x%s)",
+                logger.debug("Attribute type=2 (AS_PATH, flags:0x%s)",
                              binascii.b2a_hex(attr_flags_hex))
-                logger.debug("Attribute value = 0x%s", binascii.b2a_hex(attr_value_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
             elif attr_type_code == 3:
-                logger.debug("Attribute type = 3 (NEXT_HOP, flags:0x%s)",
+                logger.debug("Attribute type=3 (NEXT_HOP, flags:0x%s)",
                              binascii.b2a_hex(attr_flags_hex))
-                logger.debug("Attribute value = 0x%s", binascii.b2a_hex(attr_value_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
             elif attr_type_code == 4:
-                logger.debug("Attribute type = 4 (MULTI_EXIT_DISC, flags:0x%s)",
+                logger.debug("Attribute type=4 (MULTI_EXIT_DISC, flags:0x%s)",
                              binascii.b2a_hex(attr_flags_hex))
-                logger.debug("Attribute value = 0x%s", binascii.b2a_hex(attr_value_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
             elif attr_type_code == 5:
-                logger.debug("Attribute type = 5 (LOCAL_PREF, flags:0x%s)",
+                logger.debug("Attribute type=5 (LOCAL_PREF, flags:0x%s)",
                              binascii.b2a_hex(attr_flags_hex))
-                logger.debug("Attribute value = 0x%s", binascii.b2a_hex(attr_value_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
             elif attr_type_code == 6:
-                logger.debug("Attribute type = 6 (ATOMIC_AGGREGATE, flags:0x%s)",
+                logger.debug("Attribute type=6 (ATOMIC_AGGREGATE, flags:0x%s)",
                              binascii.b2a_hex(attr_flags_hex))
-                logger.debug("Attribute value = 0x%s", binascii.b2a_hex(attr_value_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
             elif attr_type_code == 7:
-                logger.debug("Attribute type = 7 (AGGREGATOR, flags:0x%s)",
+                logger.debug("Attribute type=7 (AGGREGATOR, flags:0x%s)",
+                             binascii.b2a_hex(attr_flags_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
+            elif attr_type_code == 9:  # rfc4456#section-8
+                logger.debug("Attribute type=9 (ORIGINATOR_ID, flags:0x%s)",
+                             binascii.b2a_hex(attr_flags_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
+            elif attr_type_code == 10:  # rfc4456#section-8
+                logger.debug("Attribute type=10 (CLUSTER_LIST, flags:0x%s)",
                              binascii.b2a_hex(attr_flags_hex))
-                logger.debug("Attribute value = 0x%s", binascii.b2a_hex(attr_value_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
             elif attr_type_code == 14:  # rfc4760#section-3
-                logger.debug("Attribute type = 14 (MP_REACH_NLRI, flags:0x%s)",
+                logger.debug("Attribute type=14 (MP_REACH_NLRI, flags:0x%s)",
                              binascii.b2a_hex(attr_flags_hex))
-                logger.debug("Attribute value = 0x%s", binascii.b2a_hex(attr_value_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
                 address_family_identifier_hex = attr_value_hex[0:2]
-                logger.debug("  Address Family Identifier = 0x%s",
+                logger.debug("  Address Family Identifier=0x%s",
                              binascii.b2a_hex(address_family_identifier_hex))
                 subsequent_address_family_identifier_hex = attr_value_hex[2]
-                logger.debug("  Subsequent Address Family Identifier = 0x%s",
+                logger.debug("  Subsequent Address Family Identifier=0x%s",
                              binascii.b2a_hex(subsequent_address_family_identifier_hex))
                 next_hop_netaddr_len_hex = attr_value_hex[3]
                 next_hop_netaddr_len = int(binascii.b2a_hex(next_hop_netaddr_len_hex), 16)
-                logger.debug("  Length of Next Hop Network Address = 0x%s (%s)",
-                             binascii.b2a_hex(next_hop_netaddr_len_hex),
-                             next_hop_netaddr_len)
+                logger.debug("  Length of Next Hop Network Address=%s (0x%s)",
+                             next_hop_netaddr_len,
+                             binascii.b2a_hex(next_hop_netaddr_len_hex))
                 next_hop_netaddr_hex = attr_value_hex[4:4 + next_hop_netaddr_len]
-                logger.debug("  Network Address of Next Hop = 0x%s",
-                             binascii.b2a_hex(next_hop_netaddr_hex))
+                next_hop_netaddr = ".".join(str(i) for i in struct.unpack("BBBB", next_hop_netaddr_hex))
+                logger.debug("  Network Address of Next Hop=%s (0x%s)",
+                             next_hop_netaddr, binascii.b2a_hex(next_hop_netaddr_hex))
                 reserved_hex = attr_value_hex[4 + next_hop_netaddr_len]
-                logger.debug("  Reserved = 0x%s",
+                logger.debug("  Reserved=0x%s",
                              binascii.b2a_hex(reserved_hex))
                 nlri_hex = attr_value_hex[4 + next_hop_netaddr_len + 1:]
-                logger.debug("  Network Layer Reachability Information = 0x%s",
+                logger.debug("  Network Layer Reachability Information=0x%s",
                              binascii.b2a_hex(nlri_hex))
                 nlri_prefix_list = get_prefix_list_from_hex(nlri_hex)
                 logger.debug("  NLRI prefix list: %s", nlri_prefix_list)
@@ -1244,17 +1298,17 @@ class ReadTracker(object):
                     logger.debug("  nlri_prefix_received: %s", prefix)
                 self.prefixes_introduced += len(nlri_prefix_list)  # update counter
             elif attr_type_code == 15:  # rfc4760#section-4
-                logger.debug("Attribute type = 15 (MP_UNREACH_NLRI, flags:0x%s)",
+                logger.debug("Attribute type=15 (MP_UNREACH_NLRI, flags:0x%s)",
                              binascii.b2a_hex(attr_flags_hex))
-                logger.debug("Attribute value = 0x%s", binascii.b2a_hex(attr_value_hex))
+                logger.debug("Attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
                 address_family_identifier_hex = attr_value_hex[0:2]
-                logger.debug("  Address Family Identifier = 0x%s",
+                logger.debug("  Address Family Identifier=0x%s",
                              binascii.b2a_hex(address_family_identifier_hex))
                 subsequent_address_family_identifier_hex = attr_value_hex[2]
-                logger.debug("  Subsequent Address Family Identifier = 0x%s",
+                logger.debug("  Subsequent Address Family Identifier=0x%s",
                              binascii.b2a_hex(subsequent_address_family_identifier_hex))
                 wd_hex = attr_value_hex[3:]
-                logger.debug("  Withdrawn Routes = 0x%s",
+                logger.debug("  Withdrawn Routes=0x%s",
                              binascii.b2a_hex(wd_hex))
                 wdr_prefix_list = get_prefix_list_from_hex(wd_hex)
                 logger.debug("  Withdrawn routes prefix list: %s",
@@ -1263,9 +1317,9 @@ class ReadTracker(object):
                     logger.debug("  withdrawn_prefix_received: %s", prefix)
                 self.prefixes_withdrawn += len(wdr_prefix_list)  # update counter
             else:
-                logger.debug("Unknown attribute type = %s, flags:0x%s)", attr_type_code,
+                logger.debug("Unknown attribute type=%s, flags:0x%s)", attr_type_code,
                              binascii.b2a_hex(attr_flags_hex))
-                logger.debug("Unknown attribute value = 0x%s", binascii.b2a_hex(attr_value_hex))
+                logger.debug("Unknown attribute value=0x%s", binascii.b2a_hex(attr_value_hex))
         return None
 
     def decode_update_message(self, msg):