JSONRPC PROJECT BASIC TC 01/52801/8
authorSreekalyan Devaraj <kalyan.ds@gmail.com>
Fri, 3 Mar 2017 22:33:32 +0000 (14:33 -0800)
committerSreekalyan Devaraj <kalyan.ds@gmail.com>
Thu, 30 Mar 2017 21:35:38 +0000 (14:35 -0700)
   Addressing comments by Luis
   Changing sequence of operations on test case
   Adding a keyword to parse output
   Adding a new variable for read service
   Fixing tidy issues
Change-Id: I55f4dfd90afa75ac74958519ccd3c92597a3a4fc
Signed-off-by: Sreekalyan Devaraj <kalyan.ds@gmail.com>
csit/libraries/JsonrpcKeywords.robot [new file with mode: 0644]
csit/suites/jsonrpc/010-mdsal-jsonrpc-basic.robot [new file with mode: 0644]
csit/testplans/jsonrpc-basic.txt [new file with mode: 0644]
csit/variables/jsonrpc/JsonrpcVariables.robot [new file with mode: 0644]
csit/variables/jsonrpc/interfaces_data.json [new file with mode: 0644]
csit/variables/jsonrpc/interfaces_module.json [new file with mode: 0644]
csit/variables/jsonrpc/odl-jsonrpc-test-read [new file with mode: 0755]
csit/variables/jsonrpc/readservice_peer_payload.json [new file with mode: 0644]

diff --git a/csit/libraries/JsonrpcKeywords.robot b/csit/libraries/JsonrpcKeywords.robot
new file mode 100644 (file)
index 0000000..d8bd1db
--- /dev/null
@@ -0,0 +1,62 @@
+*** Settings ***
+Library           OperatingSystem
+Library           SSHLibrary
+Library           Collections
+Library           RequestsLibrary
+Resource          ClusterManagement.robot
+Resource          ../variables/jsonrpc/JsonrpcVariables.robot
+Resource          ../variables/Variables.robot
+Resource          Utils.robot
+
+*** Keywords ***
+Run Read Service Python Script on Controller Vm
+    [Arguments]    ${host_index}=${FIRST_CONTROLLER_INDEX}    ${ub_system}=${FALSE}
+    [Documentation]    This keyword installs pip,zmq,pyzmq and starts the read service on controller vm
+    ${cmd}    Builtin.Set Variable If    ${ub_system}    ${UB_PIP}    ${CENTOS_PIP}
+    ${host_index}    Builtin.Convert To Integer    ${host_index}
+    ${host}    ClusterManagement.Resolve IP Address For Member    ${host_index}
+    ${connections}    SSHKeywords.Open_Connection_To_ODL_System    ${host}
+    SSHLibrary.Switch Connection    ${connections}
+    SSHLibrary.Put File    ${READ_SERVICE_SCRIPT}    ${WORKSPACE}/${BUNDLEFOLDER}/    mode=664
+    ${stdout}    ${stderr}    ${rc}=    SSHLibrary.Execute Command    ${cmd}    return_stdout=True    return_stderr=True
+    ...    return_rc=True
+    Log    ${stdout}
+    ${stdout}    ${stderr}    ${rc}=    SSHLibrary.Execute Command    sudo pip install --upgrade pip    return_stdout=True    return_stderr=True
+    ...    return_rc=True
+    Log    ${stdout}
+    ${stdout}    ${stderr}    ${rc}=    SSHLibrary.Execute Command    sudo pip install zmq pyzmq    return_stdout=True    return_stderr=True
+    ...    return_rc=True
+    Log    ${stdout}
+    ${module}    OperatingSystem.Get File    ${INTERFACES_MODULE_JSON}
+    ${data}    OperatingSystem.Get File    ${INTERFACES_DATA_JSON}
+    ${cmd}    Builtin.Set Variable    nohup python ${WORKSPACE}/${BUNDLEFOLDER}/odl-jsonrpc-test-read tcp://0.0.0.0:${DEFAULT_PORT} 'config' ${DEFAULT_ENDPOINT} '${module}' '${data}'
+    Log    ${cmd}
+    ${stdout}    SSHLibrary.Write    echo | rm -rf nohup.out
+    ${stdout}    SSHLibrary.Write    echo | nohup python ${WORKSPACE}/${BUNDLEFOLDER}/odl-jsonrpc-test-read tcp://0.0.0.0:${DEFAULT_PORT} 'config' ${DEFAULT_ENDPOINT} '${module}' '${data}' &
+    ${stdout}    SSHLibrary.Write    echo
+    Log    ${stdout}
+    ${stdout}    SSHLibrary.Execute Command    cat nohup.out
+    Log    ${stdout}
+    SSHLibrary.Close_Connection
+
+Mount Read Service Endpoint
+    [Arguments]    ${controller_index}=${FIRST_CONTROLLER_INDEX}    ${file}=${READ_SERVICE_PEER_PAYLOAD}    ${endpoint}=${DEFAULT_ENDPOINT}
+    [Documentation]    This keyword mounts an endpoint after starting service
+    ${JSON1}    OperatingSystem.Get File    ${file}
+    Log    ${JSON1}
+    ${response_json}    ClusterManagement.Put_As_Json_To_Member    ${READ_SERVICE_PEER_PUT_URL}${endpoint}    ${JSON1}    ${controller_index}
+    Builtin.Log    ${response_json}
+
+Verify Data On Mounted Endpoint
+    [Arguments]    ${controller_index}=${FIRST_CONTROLLER_INDEX}    ${endpoint}=${DEFAULT_ENDPOINT}
+    [Documentation]    This keyword verifies if the data we get on the mount point is same as what we put
+    ${resp}    ClusterManagement.Get_From_Member    ${READ_SERVICE_PEER_GET_1}${endpoint}${READ_SERVICE_PEER_GET_2}    ${controller_index}
+    ${response_json}    Builtin.Convert To String    ${resp}
+    Log    ${response_json}
+    Verify Restconf Get On Mounted Endpoint    ${response_json}    ${READSERVICE_NAME}    ${INTERFACE_MODULE_DEFAULT_DATA}
+
+Verify Restconf Get On Mounted Endpoint
+    [Arguments]    ${output}    ${name}    ${type}
+    [Documentation]    This keyword parses restconf get of mountpoint
+    Builtin.Should Match Regexp    ${output}    "name":"${name}"
+    Builtin.Should Match Regexp    ${output}    "type":"${type}"
diff --git a/csit/suites/jsonrpc/010-mdsal-jsonrpc-basic.robot b/csit/suites/jsonrpc/010-mdsal-jsonrpc-basic.robot
new file mode 100644 (file)
index 0000000..93bcdfd
--- /dev/null
@@ -0,0 +1,15 @@
+*** Settings ***
+Documentation     Test suite for verifying basic variations of export API including checking statuses
+Suite Setup       ClusterManagement Setup
+Suite Teardown    Delete All Sessions
+Library           OperatingSystem
+Library           DateTime
+Resource          ../../libraries/JsonrpcKeywords.robot
+
+*** Test Cases ***
+Push MDSAL data and Verify Through Restconf
+    [Documentation]    Push data using python utility and verify using restconf
+    [Tags]    Basic data
+    JsonrpcKeywords.Mount Read Service Endpoint
+    JsonrpcKeywords.Run Read Service Python Script on Controller Vm
+    JsonrpcKeywords.Verify Data On Mounted Endpoint
diff --git a/csit/testplans/jsonrpc-basic.txt b/csit/testplans/jsonrpc-basic.txt
new file mode 100644 (file)
index 0000000..e3f107d
--- /dev/null
@@ -0,0 +1 @@
+test/csit/suites/jsonrpc/010-mdsal-jsonrpc-basic.txt
diff --git a/csit/variables/jsonrpc/JsonrpcVariables.robot b/csit/variables/jsonrpc/JsonrpcVariables.robot
new file mode 100644 (file)
index 0000000..12ee618
--- /dev/null
@@ -0,0 +1,19 @@
+*** Settings ***
+Documentation     Resource consisting purely of variable definitions useful for multiple project suites.
+
+*** Variables ***
+${INTERFACES_MODULE_JSON}    ${CURDIR}/interfaces_module.json
+${INTERFACES_DATA_JSON}    ${CURDIR}/interfaces_data.json
+${READ_SERVICE_PEER_PUT_URL}    /restconf/config/jsonrpc:config/configured-endpoints/
+${READ_SERVICE_PEER_PAYLOAD}    ${CURDIR}/readservice_peer_payload.json
+${READ_SERVICE_PEER_GET_1}    /restconf/config/jsonrpc:config/configured-endpoints/
+${READ_SERVICE_PEER_GET_2}    /yang-ext:mount/ietf-interfaces:interfaces/
+${DEFAULT_ENDPOINT}    foo
+${DEFAULT_PORT}    4444
+${DEFAULT_PUT_MODULE}    ietf-interfaces
+${FIRST_CONTROLLER_INDEX}    1
+${CENTOS_PIP}     sudo yum -y install python-pip
+${UB_PIP}         sudo apt-get install -y python-pip
+${READ_SERVICE_SCRIPT}    ${CURDIR}/odl-jsonrpc-test-read
+${INTERFACE_MODULE_DEFAULT_DATA}    ietf-interfaces:other
+${READSERVICE_NAME}    test
diff --git a/csit/variables/jsonrpc/interfaces_data.json b/csit/variables/jsonrpc/interfaces_data.json
new file mode 100644 (file)
index 0000000..ac253ea
--- /dev/null
@@ -0,0 +1,6 @@
+{
+ "interface": [{
+  "name": "test",
+  "type": "other"
+ }]
+}
diff --git a/csit/variables/jsonrpc/interfaces_module.json b/csit/variables/jsonrpc/interfaces_module.json
new file mode 100644 (file)
index 0000000..7155454
--- /dev/null
@@ -0,0 +1,3 @@
+{
+ "ietf-interfaces:interfaces": {}
+}
diff --git a/csit/variables/jsonrpc/odl-jsonrpc-test-read b/csit/variables/jsonrpc/odl-jsonrpc-test-read
new file mode 100755 (executable)
index 0000000..f2fcb0b
--- /dev/null
@@ -0,0 +1,164 @@
+#!/usr/bin/env python
+
+# @author David Spence <dspence@brocade.com>
+
+# (C) 2017 Brocade Communications Systems, Inc.
+# 130 Holger Way, San Jose, CA 95134.
+# All rights reserved.
+#
+# Brocade, the B-wing symbol, Brocade Assurance, ADX, AnyIO, DCX, Fabric OS,
+# FastIron, HyperEdge, ICX, MLX, MyBrocade, NetIron, OpenScript, VCS, VDX, and
+# Vyatta are registered trademarks, and The Effortless Network and the On-Demand
+# Data Center are trademarks of Brocade Communications Systems, Inc., in the
+# United States and in other countries. Other brands and product names mentioned
+# may be trademarks of others.
+#
+# Use of the software files and documentation is subject to license terms.
+
+'''A CI test script for odl-jsonrpc.'''
+
+import logging
+from datetime import datetime
+
+import sys
+from argparse import ArgumentParser
+
+import json
+import zmq
+
+class StreamFormatter(logging.Formatter):
+    '''Provide a custom timestamp for logging.'''
+    def formatTime(self, record, datefmt=None):
+        '''Return record time as a UTC timestamp (RFC 3339 Section 5.6).'''
+        return datetime.utcfromtimestamp(record.created).isoformat('T') + 'Z'
+
+PARSE_ERROR_RESPONSE = json.dumps({
+    'jsonrpc': '2.0',
+    'error': {'code': -32700, 'message': 'Parse error'},
+    'id': None,
+})
+INVALID_REQUEST_RESPONSE = json.dumps({
+    'jsonrpc': '2.0',
+    'error': {'code': -32600, 'message': 'Invalid Request'},
+    'id': None,
+})
+
+class Service(object):
+    '''A service accepting JSON RPC Requests for the 'read' method.'''
+    def __init__(self, store, entity, path, value):
+        self._store = store
+        self._entity = entity
+        self._path = path
+        self._value = value
+        self._methods = {'read': self.read}
+    @staticmethod
+    def _parse_request(request):
+        '''If `request` passes the most basic checks for a JSON RPC Request,
+           then return its 'id', 'method' and 'params'.
+        '''
+        try:
+            version = request['jsonrpc']
+            id_ = request['id']
+            method = request['method']
+            params = request['params']
+        except KeyError:
+            raise ValueError()
+        if version != '2.0':
+            raise ValueError()
+        return (version, id_, method, params)
+    def read(self, store, entity, path):
+        '''The implementation of the `read` method for this test. If `store`,
+           `entity` and `path` do not match expected values, then return the
+           configured fixed value: otherwise raise :class:`ValueError`.
+        '''
+        if self._store != store or self._entity != entity or self._path != path:
+            raise ValueError('unexpected param values')
+        return self._value
+    def execute_json(self, string):
+        '''Execute an encoded JSON RPC Request from `string`. Return a 2-tuple
+           of (code, response), where code is process exit code and `response`
+           is an encoded JSON RPC Response. A zero exit code is returned if the
+           method implementation of this service was invoked successfully:
+           non-zero otherwise.
+        '''
+        try:
+            request = json.loads(string)
+        except ValueError:
+            return (1, PARSE_ERROR_RESPONSE)
+        try:
+            (version, id_, method, params) = self._parse_request(request)
+        except ValueError:
+            return (2, INVALID_REQUEST_RESPONSE)
+        response = {'jsonrpc': version, 'id': id_}
+        try:
+            ### assumes that params are supplied as a list for call by position
+            response['result'] = self._methods[method](*params) # pylint: disable=star-args
+        except KeyError:
+            response['error'] = {'code': -32601, 'message': 'Method not found'}
+            code = 3
+        except TypeError:
+            response['error'] = {'code': -32602, 'message': 'Invalid params'}
+            code = 4
+        except ValueError as exc:
+            response['error'] = {'code': -32603, 'message': 'Internal error'}
+            response['error']['data'] = str(exc)
+            code = 5
+        else:
+            code = 0
+        return (code, json.dumps(response))
+
+def init_logging(level):
+    '''Initialise the default logger at logging `level`.'''
+    logger = logging.getLogger()
+    logger.setLevel(level)
+    handler = logging.StreamHandler()
+    formatter = StreamFormatter(
+        fmt='%(asctime)s:%(levelname)s:%(name)s:%(message)s'
+    )
+    handler.setFormatter(formatter)
+    logger.addHandler(handler)
+
+def json_or_string(value):
+    '''If `value` is a JSON-encoded string, then return the decoded value:
+       otherwise return `value` as a string.
+    '''
+    try:
+        return json.loads(value)
+    except ValueError:
+        return str(value)
+
+def main():
+    '''Run a ZMQ REP socket on `uri` for accepting a single JSON RPC Request.
+
+       To successfully invoke the 'read' method, the Request 'params' must be
+       [`store`, `entity`, `path`] with the exact same values as were specified
+       in the command line args. On success, a JSON RPC Response with 'result'
+       `value` will be returned: otherwise, a JSON RPC Response with 'error'
+       will be returned.
+    '''
+    init_logging(logging.INFO)
+
+    aparser = ArgumentParser(description=main.__doc__)
+    aparser.add_argument('uri')
+    aparser.add_argument('store', type=json_or_string)
+    aparser.add_argument('entity', type=json_or_string)
+    aparser.add_argument('path', type=json_or_string)
+    aparser.add_argument('value', type=json_or_string)
+    args = aparser.parse_args()
+
+    service = Service(args.store, args.entity, args.path, args.value)
+
+    zock = zmq.Context().socket(zmq.REP) # pylint: disable=no-member
+    zock.bind(args.uri)
+    logging.info('ZMQ REP listening on %s', args.uri)
+    request = zock.recv()
+    logging.info('>%s', request)
+    (code, response) = service.execute_json(request)
+    logging.info('<%s', response)
+    zock.send(response)
+    logging.info('exiting with code %d', code)
+    zock.close()
+    sys.exit(code)
+
+if __name__ == '__main__':
+    main()
diff --git a/csit/variables/jsonrpc/readservice_peer_payload.json b/csit/variables/jsonrpc/readservice_peer_payload.json
new file mode 100644 (file)
index 0000000..f172107
--- /dev/null
@@ -0,0 +1,14 @@
+{
+ "configured-endpoints": {
+  "name": "foo",
+  "modules": ["ietf-interfaces"],
+  "data-config-endpoints": [{
+   "path": "{}",
+   "endpoint-uri": "zmq://127.0.0.1:4444"
+  }],
+  "data-operational-endpoints": [{
+   "path": "{}",
+   "endpoint-uri": "zmq://127.0.0.1:4444"
+  }]
+ }
+}