Restconf change event notification subscription basic test 20/33620/14
authorRadovan Sajben <rsajben@cisco.com>
Wed, 27 Jan 2016 11:55:45 +0000 (12:55 +0100)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 29 Jan 2016 14:16:07 +0000 (14:16 +0000)
- check basic notification functionality
- cover bug_3934

Change-Id: Iff408ef9a3fb26a84dfbd110578737f13b7b427a
Signed-off-by: Radovan Sajben <rsajben@cisco.com>
csit/suites/netconf/notifications/notifications_basic.robot [new file with mode: 0644]
csit/suites/netconf/notifications/templates/config_data.xml [new file with mode: 0644]
csit/suites/netconf/notifications/templates/subscribe.xml [new file with mode: 0644]
csit/testplans/netconf-userfeatures.txt
tools/wstools/wsreceiver.py [new file with mode: 0644]

diff --git a/csit/suites/netconf/notifications/notifications_basic.robot b/csit/suites/netconf/notifications/notifications_basic.robot
new file mode 100644 (file)
index 0000000..d8007c8
--- /dev/null
@@ -0,0 +1,157 @@
+*** Settings ***
+Documentation     Basic tests for BGP application peer.
+...
+...               Copyright (c) 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 subscribtion case for data store notifications.
+...               For procedure description see the
+...               https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Restconf:Change_event_notification_subscription
+...
+...               Covered bugs:
+...               Bug 3934 - Websockets: Scope ONE doesn't work correctly
+...
+...               TODO: Use cars/people model for data
+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           SSHLibrary    timeout=10s
+Library           RequestsLibrary
+Library           Collections
+Library           ${CURDIR}/../../../libraries/HsfJson/hsf_json.py
+Variables         ${CURDIR}/../../../variables/Variables.py
+Resource          ${CURDIR}/../../../libraries/ConfigViaRestconf.robot
+Resource          ${CURDIR}/../../../libraries/FailFast.robot
+Resource          ${CURDIR}/../../../libraries/KarafKeywords.robot
+Resource          ${CURDIR}/../../../libraries/SetupUtils.robot
+Resource          ${CURDIR}/../../../libraries/SSHKeywords.robot
+Resource          ${CURDIR}/../../../libraries/Utils.robot
+Resource          ${CURDIR}/../../../libraries/WaitForFailure.robot
+
+*** Variables ***
+${TEMPLATE_FOLDER}    ${CURDIR}/templates
+${RESTCONF_SUBSCRIBE_URI}    restconf/operations/sal-remote:create-data-change-event-subscription
+${RESTCONF_SUBSCRIBE_DATA}    subscribe.xml
+${RESTCONF_GET_SUBSCRIPTION_URI}    restconf/streams/stream/opendaylight-inventory:nodes/datastore=CONFIGURATION/scope=BASE
+${RESTCONF_CONFIG_URI}    restconf/config
+${RESTCONF_CONFIG_DATA}    config_data.xml
+${RECEIVER_LOG_FILE}    wsreceiver.log
+${RECEIVER_OPTIONS}    ${EMPTY}
+${CONTROLLER_LOG_LEVEL}    INFO
+
+*** Test Cases ***
+Clean_Config
+    [Documentation]    Delete item in configuration.
+    [Tags]    critical
+    BuiltIn.Log    ${CONFIG_NODES_API}
+    ${resp}=    RequestsLibrary.Delete_Request    restconf    ${CONFIG_NODES_API}    headers=${SEND_ACCEPT_XML_HEADERS}
+    Log_Response    ${resp}
+    BuiltIn.Should_Be_Equal_As_Strings    ${resp.status_code}    200
+
+Create_Subscribtion
+    [Documentation]    Subscribe for notifications.
+    [Tags]    critical
+    ${body}=    OperatingSystem.Get_File    ${TEMPLATE_FOLDER}/${RESTCONF_SUBSCRIBE_DATA}
+    BuiltIn.Log    ${RESTCONF_SUBSCRIBE_URI}
+    BuiltIn.Log    ${body}
+    ${resp}=    RequestsLibrary.Post_Request    restconf    ${RESTCONF_SUBSCRIBE_URI}    headers=${SEND_ACCEPT_XML_HEADERS}    data=${body}
+    Log_Response    ${resp}
+    BuiltIn.Should_Be_Equal_As_Strings    ${resp.status_code}    200
+
+Check_Subscribtion
+    [Documentation]    Get & check subscribtion ...
+    [Tags]    critical
+    ${resp}=    RequestsLibrary.Get_Request    restconf    ${RESTCONF_GET_SUBSCRIPTION_URI}    headers=${SEND_ACCEPT_XML_HEADERS}
+    Log_Response    ${resp}
+    BuiltIn.Should_Be_Equal_As_Strings    ${resp.status_code}    200    Response    status code error
+    ${location}=    Collections.Get_From_Dictionary    ${resp.headers}    location
+    BuiltIn.Log    ${location}
+    BuiltIn.Set_Suite_Variable    ${location}
+
+Start_Receiver
+    [Documentation]    Start the websocket listener
+    ${output}=    SSHLibrary.Write    python wsreceiver.py --uri ${location} --count 2 --logfile ${RECEIVER_LOG_FILE} ${RECEIVER_OPTIONS}
+    BuiltIn.Log    ${output}
+    ${output}=    SSHLibrary.Read    delay=2s
+    BuiltIn.Log    ${output}
+
+Change_Config
+    [Documentation]    Make a change in configuration.
+    [Tags]    critical
+    ${body}=    OperatingSystem.Get_File    ${TEMPLATE_FOLDER}/${RESTCONF_CONFIG_DATA}
+    BuiltIn.Log    ${RESTCONF_CONFIG_URI}
+    BuiltIn.Log    ${body}
+    ${resp}=    RequestsLibrary.Post_Request    restconf    ${RESTCONF_CONFIG_URI}    headers=${SEND_ACCEPT_XML_HEADERS}    data=${body}
+    Log_Response    ${resp}
+    BuiltIn.Should_Be_Equal_As_Strings    ${resp.status_code}    204
+    BuiltIn.Log    ${CONFIG_NODES_API}
+    ${resp}=    RequestsLibrary.Delete_Request    restconf    ${CONFIG_NODES_API}    headers=${SEND_ACCEPT_XML_HEADERS}
+    Log_Response    ${resp}
+    BuiltIn.Should_Be_Equal_As_Strings    ${resp.status_code}    200
+
+Check_Create_Notification
+    [Documentation]    Check the websocket listener log for a change notification.
+    [Tags]    critical
+    ${notification}=    SSHLibrary.Execute_Command    cat ${RECEIVER_LOG_FILE}
+    BuiltIn.Log    ${notification}
+    BuiltIn.Set_Suite_Variable    ${notification}
+    BuiltIn.Should_Contain    ${notification}    <notification xmlns=
+    BuiltIn.Should_Contain    ${notification}    <eventTime>
+    BuiltIn.Should_Contain    ${notification}    <data-changed-notification xmlns=
+    BuiltIn.Should_Contain    ${notification}    <operation>created</operation>
+    BuiltIn.Should_Contain    ${notification}    </data-change-event>
+    BuiltIn.Should_Contain    ${notification}    </data-changed-notification>
+    BuiltIn.Should_Contain    ${notification}    </notification>
+
+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}
+    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
+    SSHLibrary.Set_Default_Configuration    prompt=${TOOLS_SYSTEM_PROMPT}
+    SSHLibrary.Open_Connection    ${TOOLS_SYSTEM_IP}    alias=receiver
+    Utils.Flexible_Mininet_Login
+    SSHLibrary.Put_File    ${CURDIR}/../../../../tools/wstools/wsreceiver.py
+    ${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
+    RequestsLibrary.Create Session    restconf    http://${CONTROLLER}:${RESTCONFPORT}    auth=${AUTH}
+    BuiltIn.Log    http://${CONTROLLER}:${RESTCONFPORT}
+    KarafKeywords.Execute_Controller_Karaf_Command_On_Background    log:set ${CONTROLLER_LOG_LEVEL}
+
+Teardown_Everything
+    [Documentation]    Close connections.
+    ...    Tear down imported Resources.
+    ConfigViaRestconf.Teardown_Config_Via_Restconf
+    RequestsLibrary.Delete_All_Sessions
+    SSHLibrary.Close_All_Connections
+
+Log_Response
+    [Arguments]    ${resp}
+    [Documentation]    Log response.
+    BuiltIn.Log    ${resp}
+    BuiltIn.Log    ${resp.headers}
+    BuiltIn.Log    ${resp.content}
diff --git a/csit/suites/netconf/notifications/templates/config_data.xml b/csit/suites/netconf/notifications/templates/config_data.xml
new file mode 100644 (file)
index 0000000..d64b66d
--- /dev/null
@@ -0,0 +1,5 @@
+<nodes xmlns="urn:opendaylight:inventory">
+  <node>
+    <id>agent-007</id>
+  </node>
+</nodes>
diff --git a/csit/suites/netconf/notifications/templates/subscribe.xml b/csit/suites/netconf/notifications/templates/subscribe.xml
new file mode 100644 (file)
index 0000000..cb9eb12
--- /dev/null
@@ -0,0 +1,5 @@
+<input xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
+  <path xmlns:a="urn:opendaylight:inventory">/a:nodes</path>
+  <datastore xmlns="urn:sal:restconf:event:subscription">CONFIGURATION</datastore>
+  <scope xmlns="urn:sal:restconf:event:subscription">BASE</scope>
+</input>
index cf778413f0261c6078043cc626a9c0f24a441b7c..74a91eb247e2563e84d8658d5a51c114f057d3cd 100644 (file)
@@ -8,4 +8,5 @@
 integration/test/csit/suites/netconf/ready
 integration/test/csit/suites/netconf/MDSAL
 integration/test/csit/suites/netconf/CRUD
+integration/test/csit/suites/netconf/notifications
 integration/test/csit/suites/controller/NETCONF
diff --git a/tools/wstools/wsreceiver.py b/tools/wstools/wsreceiver.py
new file mode 100644 (file)
index 0000000..898cead
--- /dev/null
@@ -0,0 +1,95 @@
+"""WebSocket data receiver.
+
+The tool receives and logs data from specified URI"""
+
+# Copyright (c) 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
+
+import argparse
+import logging
+from websocket import create_connection
+
+__author__ = "Radovan Sajben"
+__copyright__ = "Copyright(c) 2016, Cisco Systems, Inc."
+__license__ = "Eclipse Public License v1.0"
+__email__ = "rsajben@cisco.com"
+
+
+def parse_arguments():
+    """Use argparse to get arguments,
+
+    Returns:
+        :return: args object
+    """
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--uri", default="ws://127.0.0.1:8185/", help="URI to connect")
+    parser.add_argument("--count", type=int, default=1, help="Number of messages to receive")
+    parser.add_argument("--logfile", default="wsreceiver.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
+
+
+class WSReceiver(object):
+    """Class which receives web socket messages."""
+
+    def __init__(self, uri):
+        """Initialise and connect to URI.
+
+        Arguments:
+            :uri: uri to connect to
+        Returns:
+            None
+        """
+        self.uri = uri
+        logger.info("Connecting to: %s", self.uri)
+        self.ws = create_connection(self.uri)
+
+    def close(self):
+        """Close the connection.
+
+        Arguments:
+            None
+        Returns:
+            None
+        """
+        logger.info("Disconnecting from: %s", self.uri)
+        self.ws.close()
+
+    def receive(self):
+        """Receive a message.
+
+        Arguments:
+            None
+        Returns:
+            :return: received data
+        """
+        data = self.ws.recv()
+        logger.info("Data received:\n%s", data)
+        return data
+
+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)
+    receiver = WSReceiver(args.uri)
+    remains = args.count
+    logger.info("Expected %d message(s)", remains)
+    while remains:
+        logger.info("Waiting for a message ...")
+        data = receiver.receive()
+        remains -= 1
+        logger.info("Remaining messages to receive: %d", remains)
+    logger.info("Finished ...")
+    receiver.close()