Add RESTCONF RFC8040 SSE test 24/92124/33
authorLuis Gomez <ecelgp@gmail.com>
Thu, 13 Aug 2020 03:23:03 +0000 (20:23 -0700)
committerVenkatrangan Govindarajan <gvrangan@gmail.com>
Tue, 18 Aug 2020 10:04:46 +0000 (10:04 +0000)
The new test is executed when using RFC8040. And it is available
in Aluminium onwards.

Change-Id: Iaf74ff1d119a93d8c87227e9fc0a9c25c1fd7a04
Signed-off-by: Luis Gomez <ecelgp@gmail.com>
csit/suites/netconf/notifications/notifications_basic.robot
csit/testplans/netconf-userfeatures-rfc8040-magnesium.txt [new file with mode: 0644]
csit/testplans/netconf-userfeatures-rfc8040-sodium.txt [new file with mode: 0644]
tools/wstools/ssereceiver.py [new file with mode: 0644]

index 0a1371641d9300a560937e2ac5a744123056a73d..f6929c5ff336213bcf7724610ee8b1f2b36733ac 100644 (file)
@@ -46,79 +46,64 @@ Resource          ${CURDIR}/../../../variables/Variables.robot
 
 *** Variables ***
 ${TEMPLATE_FOLDER}    ${CURDIR}/templates
-${RESTCONF_SUBSCRIBE_URI}    restconf/operations/sal-remote:create-data-change-event-subscription
-${RESTCONF_SUBSCRIBE_DATA}    subscribe.xml
+${DRAFT_STREAMS_URI}    restconf/streams
+${RFC8040_STREAMS_URI}    rests/data/ietf-restconf-monitoring:restconf-state/streams
 ${NODES_STREAM_PATH}    opendaylight-inventory:nodes/datastore=CONFIGURATION/scope=BASE
-${RESTCONF_GET_SUBSCRIPTION_URI}    restconf/streams/stream/data-change-event-subscription/${NODES_STREAM_PATH}
-${RFC8040_NOTIFICATIONS_STREAMS_URI}    rests/data/ietf-restconf-monitoring:restconf-state/streams
-${RFC8040_GET_SUBSCRIPTION_URI}    ${RFC8040_NOTIFICATIONS_STREAMS_URI}/stream/data-change-event-subscription/${NODES_STREAM_PATH}
+${DRAFT_DCN_STREAM_URI}    ${DRAFT_STREAMS_URI}/stream/data-change-event-subscription/${NODES_STREAM_PATH}
+${RFC8040_DCN_STREAM_URI}    ${RFC8040_STREAMS_URI}/stream/data-change-event-subscription/${NODES_STREAM_PATH}
+${RESTCONF_SUBSCRIBE_DATA}    subscribe.xml
 ${RESTCONF_CONFIG_DATA}    config_data.xml
-${RECEIVER_LOG_FILE}    wsreceiver.log
+${RECEIVER_LOG_FILE}    receiver.log
 ${RECEIVER_OPTIONS}    ${EMPTY}
 ${CONTROLLER_LOG_LEVEL}    INFO
 
 *** Test Cases ***
-Clean_Config
-    [Documentation]    Make sure config inventory is empty.
+Create_DCN_Stream
+    [Documentation]    Create DCN stream.
     [Tags]    critical
-    ${uri} =    Restconf.Generate URI    opendaylight-inventory:nodes    config
-    TemplatedRequests.Delete_From_Uri    uri=${uri}    additional_allowed_status_codes=${DELETED_STATUS_CODES}
-    # TODO: Rework also other test cases to use TemplatedRequests.
-
-Create_Subscription
-    [Documentation]    Subscribe for notifications.
-    [Tags]    critical
-    # check get streams url passes prior to creating a subscription
-    ${resp} =    RequestsLibrary.Get_Request    restconf    ${RFC8040_NOTIFICATIONS_STREAMS_URI}    headers=${SEND_ACCEPT_XML_HEADERS}
-    Log_Response    ${resp}
-    BuiltIn.Should_Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
+    Comment    Create DCN subscription
     ${body} =    OperatingSystem.Get_File    ${TEMPLATE_FOLDER}/${RESTCONF_SUBSCRIBE_DATA}
-    BuiltIn.Log    ${RESTCONF_SUBSCRIBE_URI}
-    BuiltIn.Log    ${body}
     ${uri} =    Restconf.Generate URI    sal-remote:create-data-change-event-subscription    rpc
     ${resp} =    RequestsLibrary.Post_Request    restconf    ${uri}    headers=${SEND_ACCEPT_XML_HEADERS}    data=${body}
     Log_Response    ${resp}
     BuiltIn.Should_Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
 
-Check_Notification_Stream
-    [Documentation]    Check any notification stream via RESTCONF is accessible
+Subscribe_To_DCN_Stream
+    [Documentation]    Subscribe to DCN streams.
     [Tags]    critical
-    ${resp} =    RequestsLibrary.Get_Request    restconf    ${RFC8040_NOTIFICATIONS_STREAMS_URI}    headers=${SEND_ACCEPT_XML_HEADERS}
-    Log_Response    ${resp}
-    BuiltIn.Should_Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
-    ${root}=    XML.Parse XML    ${resp.content}
-    ${name}=    Get Elements Texts    ${root}    stream/name
-    BuiltIn.Log    ${name[0]}
-    ${resp} =    RequestsLibrary.Get_Request    restconf    ${RFC8040_NOTIFICATIONS_STREAMS_URI}/stream=${name[0]}/access=JSON/location    headers=${SEND_ACCEPT_XML_HEADERS}
-    Log_Response    ${resp}
-    BuiltIn.Should_Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
-
-Check_Subscription
-    [Documentation]    Get & check subscription ...
-    [Tags]    critical
-    ${uri} =    Set Variable If    "${USE_RFC8040}" == "False"    ${RESTCONF_GET_SUBSCRIPTION_URI}    ${RFC8040_GET_SUBSCRIPTION_URI}
+    ${uri} =    Set Variable If    "${USE_RFC8040}" == "False"    ${DRAFT_DCN_STREAM_URI}    ${RFC8040_DCN_STREAM_URI}
     ${resp} =    RequestsLibrary.Get_Request    restconf    ${uri}    headers=${SEND_ACCEPT_XML_HEADERS}
     Log_Response    ${resp}
     BuiltIn.Should_Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
-    ${location} =    XML.Get Element Text    ${resp.content}
+    ${location} =    XML.Get_Element_Text    ${resp.content}
     BuiltIn.Log    ${location}
     BuiltIn.Log    ${resp.headers["Location"]}
     Should Contain    ${location}    ${resp.headers["Location"]}
     BuiltIn.Set_Suite_Variable    ${location}
 
+List_DCN_Streams
+    [Documentation]    List DCN streams.
+    [Tags]    critical
+    ${uri} =    BuiltIn.Set_Variable_If    "${USE_RFC8040}" == "False"    ${DRAFT_STREAMS_URI}    ${RFC8040_STREAMS_URI}
+    ${resp} =    RequestsLibrary.Get_Request    restconf    ${uri}    headers=${SEND_ACCEPT_XML_HEADERS}
+    Log_Response    ${resp}
+    BuiltIn.Should_Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
+    Comment    Stream only shows in RFC URL.
+    BuiltIn.Run_Keyword_If    "${USE_RFC8040}" == "True"    BuiltIn.Should_Contain    ${resp.text}    ${NODES_STREAM_PATH}
+
 Start_Receiver
     [Documentation]    Start the websocket listener
-    ${output} =    SSHLibrary.Write    python wsreceiver.py --uri ${location} --count 2 --logfile ${RECEIVER_LOG_FILE} ${RECEIVER_OPTIONS}
+    ${output} =    BuiltIn.Run_Keyword_If    "${USE_RFC8040}" == "False"    SSHLibrary.Write    python wsreceiver.py --uri ${location} --count 2 --logfile ${RECEIVER_LOG_FILE} ${RECEIVER_OPTIONS}
+    ...    ELSE    SSHLibrary.Write    python3 ssereceiver.py --controller ${ODL_SYSTEM_IP} --logfile ${RECEIVER_LOG_FILE}
     BuiltIn.Log    ${output}
     ${output} =    SSHLibrary.Read    delay=2s
     BuiltIn.Log    ${output}
 
-Change_Config
-    [Documentation]    Make a change in configuration.
+Change_DS_Config
+    [Documentation]    Make a change in DS configuration.
     [Tags]    critical
     ${body} =    OperatingSystem.Get_File    ${TEMPLATE_FOLDER}/${RESTCONF_CONFIG_DATA}
-    ${uri} =    Set Variable If    "${USE_RFC8040}" == "False"    ${CONFIG_API}    rests/data
-    BuiltIn.Log    ${body}
+    ${uri} =    BuiltIn.Set_Variable_If    "${USE_RFC8040}" == "False"    /restconf/config    /rests/data
     ${resp} =    RequestsLibrary.Post_Request    restconf    ${uri}    headers=${SEND_ACCEPT_XML_HEADERS}    data=${body}
     Log_Response    ${resp}
     BuiltIn.Should_Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
@@ -127,8 +112,8 @@ Change_Config
     Log_Response    ${resp}
     BuiltIn.Should_Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
 
-Check_Create_Notification
-    [Documentation]    Check the websocket listener log for a change notification.
+Check_Notification
+    [Documentation]    Check the WSS/SSE listener log for a change notification.
     [Tags]    critical
     ${notification} =    SSHLibrary.Execute_Command    cat ${RECEIVER_LOG_FILE}
     BuiltIn.Log    ${notification}
@@ -141,46 +126,38 @@ Check_Create_Notification
     BuiltIn.Should_Contain    ${notification}    </data-changed-notification>
     BuiltIn.Should_Contain    ${notification}    </notification>
 
+Check_Delete_Notification
+    [Documentation]    Check the websocket listener log for a delete notification.
+    [Tags]    critical
+    BuiltIn.Should_Contain    ${notification}    <operation>deleted</operation>
+
 Check_Bug_3934
     [Documentation]    Check the websocket listener log for the bug correction.
     [Tags]    critical
     ${data} =    OperatingSystem.Get_File    ${TEMPLATE_FOLDER}/${RESTCONF_CONFIG_DATA}
     BuiltIn.Log    ${data}
     BuiltIn.Log    ${notification}
-    ${packed_data} =    String.Remove_String    ${data}    ${SPACE}
-    ${packed_notification} =    String.Remove_String    ${notification}    ${SPACE}
+    ${packed_data} =    String.Remove_String    ${data}    \n
+    ${packed_data} =    String.Remove_String    ${packed_data}    ${SPACE}
+    ${packed_notification} =    String.Remove_String    ${notification}    \n
+    ${packed_notification} =    String.Remove_String    ${packed_notification}    \\n
+    ${packed_notification} =    String.Remove_String    ${packed_notification}    ${SPACE}
     BuiltIn.Should_Contain    ${packed_notification}    ${packed_data}
     [Teardown]    Report_Failure_Due_To_Bug    3934
 
-Check_Delete_Notification
-    [Documentation]    Check the websocket listener log for a delete notification.
-    [Tags]    critical
-    BuiltIn.Should_Contain    ${notification}    <operation>deleted</operation>
-
 *** 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
-    Disable SSE On Controller    ${CONTROLLER}
-    ClusterManagement.Stop_Members_From_List_Or_All
-    ClusterManagement.Start_Members_From_List_Or_All
     KarafKeywords.Open_Controller_Karaf_Console_On_Background
-    TemplatedRequests.Create_Default_Session
-    SSHLibrary.Set_Default_Configuration    prompt=${TOOLS_SYSTEM_PROMPT}
-    SSHLibrary.Open_Connection    ${TOOLS_SYSTEM_IP}    alias=receiver
-    SSHKeywords.Flexible_Mininet_Login
+    SSHKeywords.Open_Connection_To_Tools_System
     SSHLibrary.Put_File    ${CURDIR}/../../../../tools/wstools/wsreceiver.py
-    ${output_log}    ${error_log} =    SSHLibrary.Execute Command    sudo apt-get install -y python-pip    return_stdout=True    return_stderr=True
-    BuiltIn.Log    ${output_log}
-    BuiltIn.Log    ${error_log}
-    ${output_log} =    SSHLibrary.Execute_Command    sudo pip install websocket-client
-    BuiltIn.Log    ${output_log}
-    ${output_log} =    SSHLibrary.Execute_Command    python -c "help('modules')"
-    BuiltIn.Log    ${output_log}
-    Should Contain    ${output_log}    websocket
+    SSHLibrary.Put_File    ${CURDIR}/../../../../tools/wstools/ssereceiver.py
+    SSHLibrary.Execute Command    sudo apt-get install -y python-pip    return_stdout=True    return_stderr=True
+    SSHLibrary.Execute_Command    sudo pip install websocket-client    return_stdout=True    return_stderr=True
+    SSHLibrary.Execute Command    sudo python3 -m pip install asyncio aiohttp aiohttp-sse-client coroutine    return_stdout=True    return_stderr=True
     RequestsLibrary.Create Session    restconf    http://${ODL_SYSTEM_IP}:${RESTCONFPORT}    auth=${AUTH}
-    BuiltIn.Log    http://${ODL_SYSTEM_IP}:${RESTCONFPORT}
     KarafKeywords.Execute_Controller_Karaf_Command_On_Background    log:set ${CONTROLLER_LOG_LEVEL}
 
 Teardown_Everything
@@ -194,4 +171,4 @@ Log_Response
     [Documentation]    Log response.
     BuiltIn.Log    ${resp}
     BuiltIn.Log    ${resp.headers}
-    BuiltIn.Log    ${resp.content}
+    BuiltIn.Log    ${resp.text}
diff --git a/csit/testplans/netconf-userfeatures-rfc8040-magnesium.txt b/csit/testplans/netconf-userfeatures-rfc8040-magnesium.txt
new file mode 100644 (file)
index 0000000..3f53250
--- /dev/null
@@ -0,0 +1,8 @@
+# Place the suites in run order:
+integration/test/csit/suites/netconf/ready
+integration/test/csit/suites/netconf/apidocs
+integration/test/csit/suites/netconf/MDSAL
+integration/test/csit/suites/netconf/CRUD
+integration/test/csit/suites/netconf/CRUD-ACTION
+integration/test/csit/suites/netconf/KeyAuth
+
diff --git a/csit/testplans/netconf-userfeatures-rfc8040-sodium.txt b/csit/testplans/netconf-userfeatures-rfc8040-sodium.txt
new file mode 100644 (file)
index 0000000..3f53250
--- /dev/null
@@ -0,0 +1,8 @@
+# Place the suites in run order:
+integration/test/csit/suites/netconf/ready
+integration/test/csit/suites/netconf/apidocs
+integration/test/csit/suites/netconf/MDSAL
+integration/test/csit/suites/netconf/CRUD
+integration/test/csit/suites/netconf/CRUD-ACTION
+integration/test/csit/suites/netconf/KeyAuth
+
diff --git a/tools/wstools/ssereceiver.py b/tools/wstools/ssereceiver.py
new file mode 100644 (file)
index 0000000..72752cd
--- /dev/null
@@ -0,0 +1,77 @@
+"""Server_Sent event messages  receiver.
+
+The tool receives and logs data from specified URI via SSE"""
+
+import argparse
+import logging
+import asyncio
+import aiohttp
+from aiohttp_sse_client import client as sse_client
+
+
+async def get_events():
+    conn = aiohttp.TCPConnector()
+    auth = aiohttp.BasicAuth(args.user, args.password)
+    client = aiohttp.ClientSession(connector=conn, auth=auth)
+    async with sse_client.EventSource(
+        "http://" + args.controller + ":8181" + args.uri, session=client
+    ) as event_source:
+        try:
+            async for event in event_source:
+                logger.info(event)
+        except ConnectionError:
+            pass
+
+
+def parse_arguments():
+    """Use argparse to get arguments,
+
+    Returns:
+        :return: args object
+    """
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--controller", default="127.0.0.1", help="Controller IP")
+    parser.add_argument(
+        "--uri",
+        default="/rests/notif/data-change-event-subscription/opendaylight-inventory:nodes/datastore=CONFIGURATION/scope=BASE",
+        help="URI endpoint to connect",
+    )
+    parser.add_argument(
+        "--count", type=int, default=1, help="Number of messages to receive"
+    )
+    parser.add_argument("--user", default="admin", help="Controller User")
+    parser.add_argument("--password", default="admin", help="Controller Password")
+    parser.add_argument("--logfile", default="ssereceiver.log", help="Log file name")
+    parser.add_argument(
+        "--debug",
+        dest="loglevel",
+        action="store_const",
+        const=logging.DEBUG,
+        default=logging.INFO,
+        help="Log level",
+    )
+    args = parser.parse_args()
+    return args
+
+
+def main():
+    loop = asyncio.get_event_loop()
+    loop.run_until_complete(get_events())
+    loop.run_until_complete(asyncio.sleep(0))
+    loop.close()
+
+
+if __name__ == "__main__":
+    args = parse_arguments()
+    logger = logging.getLogger("logger")
+    log_formatter = logging.Formatter("%(asctime)s %(levelname)s: %(message)s")
+    console_handler = logging.StreamHandler()
+    file_handler = logging.FileHandler(args.logfile, mode="w")
+    console_handler.setFormatter(log_formatter)
+    file_handler.setFormatter(log_formatter)
+    logger.addHandler(console_handler)
+    logger.addHandler(file_handler)
+    logger.setLevel(args.loglevel)
+    logger.info("Starting to receive server-sent event messages")
+    logger.info(args)
+    main()