diff --git a/lib/ansible/module_utils/network/nso/nso.py b/lib/ansible/module_utils/network/nso/nso.py
index 170c4ce19f4..9af19774293 100644
--- a/lib/ansible/module_utils/network/nso/nso.py
+++ b/lib/ansible/module_utils/network/nso/nso.py
@@ -159,6 +159,17 @@ class JsonRpc(object):
resp, resp_json = self._write_call(payload)
return resp_json['result']
+ def show_config(self, path, operational=False):
+ payload = {
+ 'method': 'show_config',
+ 'params': {
+ 'path': path,
+ 'result_as': 'json',
+ 'with_oper': operational}
+ }
+ resp, resp_json = self._read_call(payload)
+ return resp_json['result']
+
def run_action(self, th, path, params=None):
if params is None:
params = {}
@@ -481,16 +492,40 @@ def connect(params):
return client
-def verify_version(client):
+def verify_version(client, required_versions=None):
+ if required_versions is None:
+ required_versions = [(4, 5), (4, 4, 3)]
+
version_str = client.get_system_setting('version')
+ if not verify_version_str(version_str, required_versions):
+ supported_versions = ', '.join(
+ ['.'.join([str(p) for p in required_version])
+ for required_version in required_versions])
+ raise ModuleFailException(
+ 'unsupported NSO version {0}. {1} or later supported'.format(
+ version_str, supported_versions))
+
+
+def verify_version_str(version_str, required_versions):
version = [int(p) for p in version_str.split('.')]
if len(version) < 2:
raise ModuleFailException(
'unsupported NSO version format {0}'.format(version_str))
- if (version[0] < 4 or version[1] < 4 or
- (version[1] == 4 and (len(version) < 3 or version[2] < 3))):
- raise ModuleFailException(
- 'unsupported NSO version {0}, only 4.4.3 or later is supported'.format(version_str))
+
+ def check_version(required_version, version):
+ for pos in range(len(required_version)):
+ if pos >= len(version):
+ return False
+ if version[pos] > required_version[pos]:
+ return True
+ if version[pos] < required_version[pos]:
+ return False
+ return True
+
+ for required_version in required_versions:
+ if check_version(required_version, version):
+ return True
+ return False
def normalize_value(expected_value, value, key):
diff --git a/lib/ansible/modules/network/nso/nso_action.py b/lib/ansible/modules/network/nso/nso_action.py
index 93abfccfcd7..74539a9e0d6 100644
--- a/lib/ansible/modules/network/nso/nso_action.py
+++ b/lib/ansible/modules/network/nso/nso_action.py
@@ -36,6 +36,8 @@ short_description: Executes Cisco NSO actions and verifies output.
description:
- This module provices support for executing Cisco NSO actions and then
verifying that the output is as expected.
+requirements:
+ - Cisco NSO version 4.4.3 or higher.
author: "Claes Nästén (@cnasten)"
options:
path:
diff --git a/lib/ansible/modules/network/nso/nso_config.py b/lib/ansible/modules/network/nso/nso_config.py
index dc0296ad3b7..1250c87c9d4 100644
--- a/lib/ansible/modules/network/nso/nso_config.py
+++ b/lib/ansible/modules/network/nso/nso_config.py
@@ -36,6 +36,8 @@ short_description: Manage Cisco NSO configuration and service synchronization.
description:
- This module provides support for managing configuration in Cisco NSO and
can also ensure services are in sync.
+requirements:
+ - Cisco NSO version 4.4.3 or higher.
author: "Claes Nästén (@cnasten)"
options:
data:
diff --git a/lib/ansible/modules/network/nso/nso_show.py b/lib/ansible/modules/network/nso/nso_show.py
new file mode 100644
index 00000000000..5eb24f63af7
--- /dev/null
+++ b/lib/ansible/modules/network/nso/nso_show.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+#
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+DOCUMENTATION = '''
+---
+module: nso_show
+extends_documentation_fragment: nso
+short_description: Displays data from Cisco NSO.
+description:
+ - This module provides support for displaying data from Cisco NSO.
+requirements:
+ - Cisco NSO version 3.4.12 or higher, 4.1.9 or higher, 4.2.6 or higher,
+ 4.3.7 or higher, 4.4.5 or higher, 4.5 or higher.
+author: "Claes Nästén (@cnasten)"
+options:
+ path:
+ description: Path to NSO data.
+ required: true
+ operational:
+ description: >
+ Controls wheter or not operational data is included in the result.
+version_added: "2.5"
+'''
+
+EXAMPLES = '''
+- name: Show devices including operational data
+ nso_show:
+ url: http://localhost:8080/jsonrpc
+ username: username
+ password: password
+ path: /ncs:devices/device
+ operational: true
+'''
+
+RETURN = '''
+output:
+ description: Configuration
+ returned: success
+ type: dict
+'''
+
+from ansible.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec
+from ansible.module_utils.network.nso.nso import ModuleFailException, NsoException
+from ansible.module_utils.basic import AnsibleModule
+
+
+class NsoShow(object):
+ REQUIRED_VERSIONS = [
+ (4, 5),
+ (4, 4, 5),
+ (4, 3, 7),
+ (4, 2, 6),
+ (4, 1, 9),
+ (3, 4, 12)
+ ]
+
+ def __init__(self, check_mode, client, path, operational):
+ self._check_mode = check_mode
+ self._client = client
+ self._path = path
+ self._operational = operational
+
+ def main(self):
+ if self._check_mode:
+ return {}
+ else:
+ return self._client.show_config(self._path, self._operational)
+
+
+def main():
+ argument_spec = dict(
+ path=dict(required=True, type='str'),
+ operational=dict(required=False, type='bool', default=False)
+ )
+ argument_spec.update(nso_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True
+ )
+ p = module.params
+
+ client = connect(p)
+ nso_show = NsoShow(
+ module.check_mode, client,
+ p['path'], p['operational'])
+ try:
+ verify_version(client, NsoShow.REQUIRED_VERSIONS)
+
+ output = nso_show.main()
+ client.logout()
+ module.exit_json(changed=False, output=output)
+ except NsoException as ex:
+ client.logout()
+ module.fail_json(msg=ex.message)
+ except ModuleFailException as ex:
+ client.logout()
+ module.fail_json(msg=ex.message)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/lib/ansible/modules/network/nso/nso_verify.py b/lib/ansible/modules/network/nso/nso_verify.py
index 95a57bffceb..74b3f76ab90 100644
--- a/lib/ansible/modules/network/nso/nso_verify.py
+++ b/lib/ansible/modules/network/nso/nso_verify.py
@@ -36,6 +36,8 @@ short_description: Verifies Cisco NSO configuration.
description:
- This module provides support for verifying Cisco NSO configuration is in
compliance with specified values.
+requirements:
+ - Cisco NSO version 4.4.3 or higher.
author: "Claes Nästén (@cnasten)"
options:
data:
diff --git a/lib/ansible/utils/module_docs_fragments/nso.py b/lib/ansible/utils/module_docs_fragments/nso.py
index 3ce8d236d6f..75a3ddf0b17 100644
--- a/lib/ansible/utils/module_docs_fragments/nso.py
+++ b/lib/ansible/utils/module_docs_fragments/nso.py
@@ -22,8 +22,6 @@
class ModuleDocFragment(object):
DOCUMENTATION = '''
-notes:
- - Cisco NSO version 4.4.3 or higher required.
options:
url:
description: NSO JSON-RPC URL, http://localhost:8080/jsonrpc
@@ -34,6 +32,4 @@ options:
password:
description: NSO password
required: true
-requirements:
- - Cisco NSO version 4.4.3 or higher
'''
diff --git a/test/units/module_utils/network/nso/test_nso.py b/test/units/module_utils/network/nso/test_nso.py
index 10224fb76f0..e41e8c2594f 100644
--- a/test/units/module_utils/network/nso/test_nso.py
+++ b/test/units/module_utils/network/nso/test_nso.py
@@ -336,3 +336,22 @@ class TestValueBuilder(unittest.TestCase):
self.assertEquals('nested-value', value.value)
self.assertEqual(0, len(calls))
+
+
+class TestVerifyVersion(unittest.TestCase):
+ def test_valid_versions(self):
+ self.assertTrue(nso.verify_version_str('5.0', [(4, 6), (4, 5, 1)]))
+ self.assertTrue(nso.verify_version_str('5.1.1', [(4, 6), (4, 5, 1)]))
+ self.assertTrue(nso.verify_version_str('5.1.1.2', [(4, 6), (4, 5, 1)]))
+ self.assertTrue(nso.verify_version_str('4.6', [(4, 6), (4, 5, 1)]))
+ self.assertTrue(nso.verify_version_str('4.6.2', [(4, 6), (4, 5, 1)]))
+ self.assertTrue(nso.verify_version_str('4.6.2.1', [(4, 6), (4, 5, 1)]))
+ self.assertTrue(nso.verify_version_str('4.5.1', [(4, 6), (4, 5, 1)]))
+ self.assertTrue(nso.verify_version_str('4.5.2', [(4, 6), (4, 5, 1)]))
+ self.assertTrue(nso.verify_version_str('4.5.1.2', [(4, 6), (4, 5, 1)]))
+
+ def test_invalid_versions(self):
+ self.assertFalse(nso.verify_version_str('4.4', [(4, 6), (4, 5, 1)]))
+ self.assertFalse(nso.verify_version_str('4.4.1', [(4, 6), (4, 5, 1)]))
+ self.assertFalse(nso.verify_version_str('4.4.1.2', [(4, 6), (4, 5, 1)]))
+ self.assertFalse(nso.verify_version_str('4.5.0', [(4, 6), (4, 5, 1)]))
diff --git a/test/units/modules/network/nso/test_nso_show.py b/test/units/modules/network/nso/test_nso_show.py
new file mode 100644
index 00000000000..75e7014e02b
--- /dev/null
+++ b/test/units/modules/network/nso/test_nso_show.py
@@ -0,0 +1,96 @@
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+
+from __future__ import (absolute_import, division, print_function)
+
+import json
+
+from ansible.compat.tests.mock import patch
+from ansible.modules.network.nso import nso_show
+from . import nso_module
+from .nso_module import MockResponse
+
+
+class TestNsoShow(nso_module.TestNsoModule):
+ module = nso_show
+
+ @patch('ansible.module_utils.network.nso.nso.open_url')
+ def test_nso_show_missing(self, open_url_mock):
+ path = '/ncs:devices/device{ce0}/missing'
+ calls = [
+ MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}),
+ MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5"}'),
+ MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'),
+ MockResponse('show_config',
+ {'path': path, 'result_as': 'json'}, 200,
+ '{"error": {"data": {"param": "path"}, "type": "rpc.method.invalid_params"}}'),
+ MockResponse('logout', {}, 200, '{"result": {}}'),
+ ]
+ open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
+
+ nso_module.set_module_args({
+ 'username': 'user', 'password': 'password',
+ 'url': 'http://localhost:8080/jsonrpc',
+ 'path': path
+ })
+ self.execute_module(failed=True, msg='NSO show_config invalid params. path = /ncs:devices/device{ce0}/missing')
+
+ self.assertEqual(0, len(calls))
+
+ @patch('ansible.module_utils.network.nso.nso.open_url')
+ def test_nso_show_config(self, open_url_mock):
+ path = '/ncs:devices/device{ce0}'
+ calls = [
+ MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}),
+ MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5"}'),
+ MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'),
+ MockResponse('show_config', {'path': path, 'result_as': 'json'}, 200, '{"result": {"data": {}}}'),
+ MockResponse('logout', {}, 200, '{"result": {}}'),
+ ]
+ open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
+
+ nso_module.set_module_args({
+ 'username': 'user', 'password': 'password',
+ 'url': 'http://localhost:8080/jsonrpc',
+ 'path': path,
+ 'operational': False
+ })
+ self.execute_module(changed=False, output={"data": {}})
+ self.assertEqual(0, len(calls))
+
+ @patch('ansible.module_utils.network.nso.nso.open_url')
+ def test_nso_show_config_and_oper(self, open_url_mock):
+ path = '/ncs:devices/device{ce0}/sync-from'
+ calls = [
+ MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}),
+ MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5"}'),
+ MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'),
+ MockResponse('show_config', {'path': path, 'result_as': 'json'}, 200, '{"result": {"data": {}}}'),
+ MockResponse('logout', {}, 200, '{"result": {}}'),
+ ]
+ open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
+
+ nso_module.set_module_args({
+ 'username': 'user', 'password': 'password',
+ 'url': 'http://localhost:8080/jsonrpc',
+ 'path': path,
+ 'operational': True
+ })
+ self.execute_module(changed=False, output={"data": {}})
+
+ self.assertEqual(0, len(calls))