From 7a0fc45a174fd8662fa472495dcd43186a8686eb Mon Sep 17 00:00:00 2001 From: Matt Mullen Date: Mon, 2 Nov 2020 15:25:33 -0500 Subject: [PATCH 1/4] cisco nso module migration --- .github/BOTMETA.yml | 8 - .gitignore | 3 + changelogs/fragments/132_remove_nso.yml | 2 + meta/runtime.yml | 35 + plugins/modules/network/nso/nso_action.py | 183 ----- plugins/modules/network/nso/nso_config.py | 282 -------- plugins/modules/network/nso/nso_query.py | 121 ---- plugins/modules/network/nso/nso_show.py | 126 ---- plugins/modules/network/nso/nso_verify.py | 200 ------ plugins/modules/nso_action.py | 1 - plugins/modules/nso_config.py | 1 - plugins/modules/nso_query.py | 1 - plugins/modules/nso_show.py | 1 - plugins/modules/nso_verify.py | 1 - tests/sanity/ignore-2.10.txt | 8 - tests/sanity/ignore-2.11.txt | 8 - tests/sanity/ignore-2.9.txt | 6 - .../module_utils/network/nso/__init__.py | 0 .../module_utils/network/nso/test_nso.py | 660 ------------------ .../plugins/modules/network/nso/__init__.py | 0 .../network/nso/fixtures/complex_schema.json | 212 ------ .../network/nso/fixtures/config_config.json | 20 - .../nso/fixtures/config_config_changes.json | 46 -- .../nso/fixtures/config_empty_data.json | 1 - .../nso/fixtures/description_schema.json | 28 - .../network/nso/fixtures/device_schema.json | 1 - .../network/nso/fixtures/devices_schema.json | 1 - .../fixtures/l3vpn_l3vpn_endpoint_schema.json | 1 - .../nso/fixtures/l3vpn_l3vpn_schema.json | 1 - .../network/nso/fixtures/l3vpn_schema.json | 1 - .../nso/fixtures/sync_from_schema.json | 178 ----- .../nso/fixtures/verify_violation_data.json | 10 - .../plugins/modules/network/nso/nso_module.py | 132 ---- .../modules/network/nso/test_nso_action.py | 167 ----- .../modules/network/nso/test_nso_config.py | 138 ---- .../modules/network/nso/test_nso_query.py | 57 -- .../modules/network/nso/test_nso_show.py | 98 --- .../modules/network/nso/test_nso_verify.py | 110 --- 38 files changed, 40 insertions(+), 2809 deletions(-) create mode 100644 changelogs/fragments/132_remove_nso.yml delete mode 100644 plugins/modules/network/nso/nso_action.py delete mode 100644 plugins/modules/network/nso/nso_config.py delete mode 100644 plugins/modules/network/nso/nso_query.py delete mode 100644 plugins/modules/network/nso/nso_show.py delete mode 100644 plugins/modules/network/nso/nso_verify.py delete mode 120000 plugins/modules/nso_action.py delete mode 120000 plugins/modules/nso_config.py delete mode 120000 plugins/modules/nso_query.py delete mode 120000 plugins/modules/nso_show.py delete mode 120000 plugins/modules/nso_verify.py delete mode 100644 tests/unit/plugins/module_utils/network/nso/__init__.py delete mode 100644 tests/unit/plugins/module_utils/network/nso/test_nso.py delete mode 100644 tests/unit/plugins/modules/network/nso/__init__.py delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/complex_schema.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/config_config.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/config_config_changes.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/config_empty_data.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/description_schema.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/device_schema.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/devices_schema.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/l3vpn_l3vpn_endpoint_schema.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/l3vpn_l3vpn_schema.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/l3vpn_schema.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/sync_from_schema.json delete mode 100644 tests/unit/plugins/modules/network/nso/fixtures/verify_violation_data.json delete mode 100644 tests/unit/plugins/modules/network/nso/nso_module.py delete mode 100644 tests/unit/plugins/modules/network/nso/test_nso_action.py delete mode 100644 tests/unit/plugins/modules/network/nso/test_nso_config.py delete mode 100644 tests/unit/plugins/modules/network/nso/test_nso_query.py delete mode 100644 tests/unit/plugins/modules/network/nso/test_nso_show.py delete mode 100644 tests/unit/plugins/modules/network/nso/test_nso_verify.py diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index f5f3a046..62bbae52 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -117,9 +117,6 @@ files: $module_utils/network/nos/nos.py: maintainers: $team_extreme labels: networking - $module_utils/network/nso/nso.py: - maintainers: $team_nso - labels: cisco networking $module_utils/network/ordnance/ordnance.py: maintainers: alexanderturner djh00t labels: networking @@ -317,10 +314,6 @@ files: $modules/network/nos/: authors: LindsayHill maintainers: bigmstone ujwalkomarla - $modules/network/nso/: - authors: cnasten - maintainers: cmoberg tbjurman - labels: cisco networking $modules/network/nuage/nuage_vspk.py: authors: pdellaert $modules/network/opx/opx_cps.py: @@ -436,5 +429,4 @@ macros: team_netscaler: chiradeep giorgos-nikolopoulos team_netvisor: Qalthos amitsi csharpe-pn pdam preetiparasar team_networking: NilashishC Qalthos danielmellado ganeshrn justjais trishnaguha - team_nso: cmoberg cnasten tbjurman team_scaleway: DenBeke QuentinBrosse abarbare jerome-quere kindermoumoute remyleo diff --git a/.gitignore b/.gitignore index 882f4f10..0c9bdcdc 100644 --- a/.gitignore +++ b/.gitignore @@ -396,4 +396,7 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk +# MacOS folder metadata +.DS_Store + # End of https://www.gitignore.io/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv diff --git a/changelogs/fragments/132_remove_nso.yml b/changelogs/fragments/132_remove_nso.yml new file mode 100644 index 00000000..ef035453 --- /dev/null +++ b/changelogs/fragments/132_remove_nso.yml @@ -0,0 +1,2 @@ +removed_features: +- "nso - remove support for Cisco NSO modules which have been migrated to the cisco.nso namespace" diff --git a/meta/runtime.yml b/meta/runtime.yml index 73ef8a74..7addef02 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -133,6 +133,31 @@ plugin_routing: deprecation: removal_version: 2.0.0 warning_text: see plugin documentation for details + nso_config: + deprecation: + removal_version: 2.0.0 + warning_text: The nso_config module has been moved to the cisco.nso collection + redirect: cisco.nso + nso_action: + deprecation: + removal_version: 2.0.0 + warning_text: The nso_action module has been moved to the cisco.nso collection + redirect: cisco.nso + nso_query: + deprecation: + removal_version: 2.0.0 + warning_text: The nso_query module has been moved to the cisco.nso collection + redirect: cisco.nso + nso_verify: + deprecation: + removal_version: 2.0.0 + warning_text: The nso_verify module has been moved to the cisco.nso collection + redirect: cisco.nso + nso_show: + deprecation: + removal_version: 2.0.0 + warning_text: The nso_show module has been moved to the cisco.nso collection + redirect: cisco.nso onyx_aaa: deprecation: removal_version: 2.0.0 @@ -331,6 +356,11 @@ plugin_routing: warning_text: The onyx terminal plugin has been moved to the mellanox.onyx collection redirect: mellanox.onyx.onyx module_utils: + network.nso: + deprecation: + removal_version: 2.0.0 + warning_text: The nso module_utils plugin has been moved to the cisco.nso collection + redirect: cisco.nso network.onyx: deprecation: removal_version: 2.0.0 @@ -348,6 +378,11 @@ plugin_routing: warning_text: The onyx_config action plugin has been moved to the mellanox.onyx collection redirect: mellanox.onyx.onyx_config doc_fragments: + nso: + deprecation: + removal_version: 2.0.0 + warning_text: The nso doc_fragments plugin has been moved to the cisco.nso collection + redirect: cisco.nso.nso onyx: deprecation: removal_version: 2.0.0 diff --git a/plugins/modules/network/nso/nso_action.py b/plugins/modules/network/nso/nso_action.py deleted file mode 100644 index 624a8bb8..00000000 --- a/plugins/modules/network/nso/nso_action.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: nso_action -extends_documentation_fragment: -- community.network.nso - -short_description: Executes Cisco NSO actions and verifies output. -description: - - This module provides support for executing Cisco NSO actions and then - verifying that the output is as expected. -requirements: - - Cisco NSO version 3.4 or higher. -author: "Claes Nästén (@cnasten)" -options: - path: - description: Path to NSO action. - required: true - input: - description: > - NSO action parameters. - output_required: - description: > - Required output parameters. - output_invalid: - description: > - List of result parameter names that will cause the task to fail if they - are present. - validate_strict: - description: > - If set to true, the task will fail if any output parameters not in - output_required is present in the output. - type: bool -''' - -EXAMPLES = ''' -- name: Sync NSO device - community.network.nso_action: - url: http://localhost:8080/jsonrpc - username: username - password: password - path: /ncs:devices/device{ce0}/sync-from - input: {} -''' - -RETURN = ''' -output: - description: Action output - returned: success - type: dict - sample: - result: true -''' - -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import normalize_value -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import ModuleFailException, NsoException -from ansible.module_utils.basic import AnsibleModule - - -class NsoAction(object): - REQUIRED_VERSIONS = [ - (3, 4) - ] - - def __init__(self, check_mode, client, - path, input, - output_required, output_invalid, validate_strict): - self._check_mode = check_mode - self._client = client - self._path = path - self._input = input - self._output_required = output_required - self._output_invalid = output_invalid - self._validate_strict = validate_strict - - def main(self): - schema = self._client.get_schema(path=self._path) - if schema['data']['kind'] != 'action': - raise ModuleFailException('{0} is not an action'.format(self._path)) - - input_schema = [c for c in schema['data']['children'] - if c.get('is_action_input', False)] - - for key, value in self._input.items(): - child = next((c for c in input_schema if c['name'] == key), None) - if child is None: - raise ModuleFailException('no parameter {0}'.format(key)) - - # implement type validation in the future - - if self._check_mode: - return {} - else: - return self._run_and_verify() - - def _run_and_verify(self): - output = self._client.run_action(None, self._path, self._input) - for key, value in self._output_required.items(): - if key not in output: - raise ModuleFailException('{0} not in result'.format(key)) - - n_value = normalize_value(value, output[key], key) - if value != n_value: - msg = '{0} value mismatch. expected {1} got {2}'.format( - key, value, n_value) - raise ModuleFailException(msg) - - for key in self._output_invalid.keys(): - if key in output: - raise ModuleFailException('{0} not allowed in result'.format(key)) - - if self._validate_strict: - for name in output.keys(): - if name not in self._output_required: - raise ModuleFailException('{0} not allowed in result'.format(name)) - - return output - - -def main(): - argument_spec = dict( - path=dict(required=True), - input=dict(required=False, type='dict', default={}), - output_required=dict(required=False, type='dict', default={}), - output_invalid=dict(required=False, type='dict', default={}), - validate_strict=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_action = NsoAction( - module.check_mode, client, - p['path'], - p['input'], - p['output_required'], - p['output_invalid'], - p['validate_strict']) - try: - verify_version(client, NsoAction.REQUIRED_VERSIONS) - - output = nso_action.main() - client.logout() - module.exit_json(changed=True, 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/plugins/modules/network/nso/nso_config.py b/plugins/modules/network/nso/nso_config.py deleted file mode 100644 index eb938251..00000000 --- a/plugins/modules/network/nso/nso_config.py +++ /dev/null @@ -1,282 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: nso_config -extends_documentation_fragment: -- community.network.nso - -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 3.4.12 or higher, 4.2.7 or higher, - 4.3.8 or higher, 4.4.3 or higher, 4.5 or higher. -author: "Claes Nästén (@cnasten)" -options: - data: - description: > - NSO data in format as | display json converted to YAML. List entries can - be annotated with a __state entry. Set to in-sync/deep-in-sync for - services to verify service is in sync with the network. Set to absent in - list entries to ensure they are deleted if they exist in NSO. - required: true -''' - -EXAMPLES = ''' -- name: Create L3VPN - community.network.nso_config: - url: http://localhost:8080/jsonrpc - username: username - password: password - data: - l3vpn:vpn: - l3vpn: - - name: company - route-distinguisher: 999 - endpoint: - - id: branch-office1 - ce-device: ce6 - ce-interface: GigabitEthernet0/12 - ip-network: 10.10.1.0/24 - bandwidth: 12000000 - as-number: 65101 - - id: branch-office2 - ce-device: ce1 - ce-interface: GigabitEthernet0/11 - ip-network: 10.7.7.0/24 - bandwidth: 6000000 - as-number: 65102 - - id: branch-office3 - __state: absent - __state: in-sync -''' - -RETURN = ''' -changes: - description: List of changes - returned: always - type: complex - sample: - - path: "/l3vpn:vpn/l3vpn{example}/endpoint{office}/bandwidth" - from: '6000000' - to: '12000000' - type: set - contains: - path: - description: Path to value changed - returned: always - type: str - from: - description: Previous value if any, else null - returned: When previous value is present on value change - type: str - to: - description: Current value if any, else null. - returned: When new value is present on value change - type: - description: Type of change. create|delete|set|re-deploy -diffs: - description: List of sync changes - returned: always - type: complex - sample: - - path: "/l3vpn:vpn/l3vpn{example}" - diff: |2 - devices { - device pe3 { - config { - alu:service { - vprn 65101 { - bgp { - group example-ce6 { - - peer-as 65102; - + peer-as 65101; - } - } - } - } - } - } - } - contains: - path: - description: keypath to service changed - returned: always - type: str - diff: - description: configuration difference triggered the re-deploy - returned: always - type: str -''' - -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import State, ValueBuilder -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import ModuleFailException, NsoException -from ansible.module_utils.basic import AnsibleModule - - -class NsoConfig(object): - REQUIRED_VERSIONS = [ - (4, 5), - (4, 4, 3), - (4, 3, 8), - (4, 2, 7), - (3, 4, 12) - ] - - def __init__(self, check_mode, client, data): - self._check_mode = check_mode - self._client = client - self._data = data - - self._changes = [] - self._diffs = [] - - def main(self): - # build list of values from configured data - value_builder = ValueBuilder(self._client) - for key, value in self._data.items(): - value_builder.build('', key, value) - - self._data_write(value_builder.values) - - # check sync AFTER configuration is written - sync_values = self._sync_check(value_builder.values) - self._sync_ensure(sync_values) - - return self._changes, self._diffs - - def _data_write(self, values): - th = self._client.get_trans(mode='read_write') - - for value in values: - if value.state == State.SET: - self._client.set_value(th, value.path, value.value) - elif value.state == State.PRESENT: - self._client.create(th, value.path) - elif value.state == State.ABSENT: - self._client.delete(th, value.path) - - changes = self._client.get_trans_changes(th) - for change in changes: - if change['op'] == 'value_set': - self._changes.append({ - 'path': change['path'], - 'from': change['old'] or None, - 'to': change['value'], - 'type': 'set' - }) - elif change['op'] in ('created', 'deleted'): - self._changes.append({ - 'path': change['path'], - 'type': change['op'][:-1] - }) - - if len(changes) > 0: - warnings = self._client.validate_commit(th) - if len(warnings) > 0: - raise NsoException( - 'failed to validate transaction with warnings: {0}'.format( - ', '.join((str(warning) for warning in warnings))), {}) - - if self._check_mode or len(changes) == 0: - self._client.delete_trans(th) - else: - self._client.commit(th) - - def _sync_check(self, values): - sync_values = [] - - for value in values: - if value.state in (State.CHECK_SYNC, State.IN_SYNC): - action = 'check-sync' - elif value.state in (State.DEEP_CHECK_SYNC, State.DEEP_IN_SYNC): - action = 'deep-check-sync' - else: - action = None - - if action is not None: - action_path = '{0}/{1}'.format(value.path, action) - action_params = {'outformat': 'cli'} - resp = self._client.run_action(None, action_path, action_params) - if len(resp) > 0: - sync_values.append( - ValueBuilder.Value(value.path, value.state, resp[0]['value'])) - - return sync_values - - def _sync_ensure(self, sync_values): - for value in sync_values: - if value.state in (State.CHECK_SYNC, State.DEEP_CHECK_SYNC): - raise NsoException( - '{0} out of sync, diff {1}'.format(value.path, value.value), {}) - - action_path = '{0}/{1}'.format(value.path, 're-deploy') - if not self._check_mode: - result = self._client.run_action(None, action_path) - if not result: - raise NsoException( - 'failed to re-deploy {0}'.format(value.path), {}) - - self._changes.append({'path': value.path, 'type': 're-deploy'}) - self._diffs.append({'path': value.path, 'diff': value.value}) - - -def main(): - argument_spec = dict( - data=dict(required=True, type='dict') - ) - argument_spec.update(nso_argument_spec) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - p = module.params - - client = connect(p) - nso_config = NsoConfig(module.check_mode, client, p['data']) - try: - verify_version(client, NsoConfig.REQUIRED_VERSIONS) - - changes, diffs = nso_config.main() - client.logout() - - changed = len(changes) > 0 - module.exit_json( - changed=changed, changes=changes, diffs=diffs) - - 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/plugins/modules/network/nso/nso_query.py b/plugins/modules/network/nso/nso_query.py deleted file mode 100644 index 2f7b53ba..00000000 --- a/plugins/modules/network/nso/nso_query.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: nso_query -extends_documentation_fragment: -- community.network.nso - -short_description: Query data from Cisco NSO. -description: - - This module provides support for querying data from Cisco NSO using XPath. -requirements: - - Cisco NSO version 3.4 or higher. -author: "Claes Nästén (@cnasten)" -options: - xpath: - description: XPath selection relative to the root. - required: true - fields: - description: > - List of fields to select from matching nodes. - required: true -''' - -EXAMPLES = ''' -- name: Select device name and description - community.network.nso_query: - url: http://localhost:8080/jsonrpc - username: username - password: password - xpath: /ncs:devices/device - fields: - - name - - description -''' - -RETURN = ''' -output: - description: Value of matching nodes - returned: success - type: list -''' - -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import ModuleFailException, NsoException -from ansible.module_utils.basic import AnsibleModule - - -class NsoQuery(object): - REQUIRED_VERSIONS = [ - (3, 4) - ] - - def __init__(self, check_mode, client, xpath, fields): - self._check_mode = check_mode - self._client = client - self._xpath = xpath - self._fields = fields - - def main(self): - if self._check_mode: - return [] - else: - return self._client.query(self._xpath, self._fields) - - -def main(): - argument_spec = dict( - xpath=dict(required=True, type='str'), - fields=dict(required=True, type='list') - ) - argument_spec.update(nso_argument_spec) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - p = module.params - - client = connect(p) - nso_query = NsoQuery( - module.check_mode, client, - p['xpath'], p['fields']) - try: - verify_version(client, NsoQuery.REQUIRED_VERSIONS) - - output = nso_query.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/plugins/modules/network/nso/nso_show.py b/plugins/modules/network/nso/nso_show.py deleted file mode 100644 index c7c05fb6..00000000 --- a/plugins/modules/network/nso/nso_show.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: nso_show -extends_documentation_fragment: -- community.network.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 whether or not operational data is included in the result. - type: bool - default: false -''' - -EXAMPLES = ''' -- name: Show devices including operational data - community.network.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_collections.community.network.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.network.plugins.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/plugins/modules/network/nso/nso_verify.py b/plugins/modules/network/nso/nso_verify.py deleted file mode 100644 index 637aa6ed..00000000 --- a/plugins/modules/network/nso/nso_verify.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: nso_verify -extends_documentation_fragment: -- community.network.nso - -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 3.4.12 or higher, 4.2.7 or higher, - 4.3.8 or higher, 4.4.3 or higher, 4.5 or higher. -author: "Claes Nästén (@cnasten)" -options: - data: - description: > - NSO data in format as C(| display json) converted to YAML. List entries can - be annotated with a C(__state) entry. Set to in-sync/deep-in-sync for - services to verify service is in sync with the network. Set to absent in - list entries to ensure they are deleted if they exist in NSO. - required: true -''' - -EXAMPLES = ''' -- name: Verify interface is up - nso_config: - url: http://localhost:8080/jsonrpc - username: username - password: password - data: - ncs:devices: - device: - - name: ce0 - live-status: - interfaces: - interface: - - name: GigabitEthernet0/12 - - state: Up -''' - -RETURN = ''' -violations: - description: List of value violations - returned: failed - type: complex - sample: - - path: /ncs:devices/device{ce0}/description - expected-value: CE0 example - value: null - contains: - path: - description: Path to the value in violation - returned: always - type: str - expected-value: - description: Expected value of path - returned: always - type: str - value: - description: Current value of path - returned: always - type: str -''' - -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import normalize_value -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import State, ValueBuilder -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import ModuleFailException, NsoException -from ansible.module_utils.basic import AnsibleModule - - -class NsoVerify(object): - REQUIRED_VERSIONS = [ - (4, 5), - (4, 4, 3), - (4, 3, 8), - (4, 2, 7), - (3, 4, 12) - ] - - def __init__(self, client, data): - self._client = client - self._data = data - - def main(self): - violations = [] - - # build list of values from configured data - value_builder = ValueBuilder(self._client, 'verify') - for key, value in self._data.items(): - value_builder.build('', key, value) - - for expected_value in value_builder.values: - if expected_value.state == State.PRESENT: - violations.append({ - 'path': expected_value.path, - 'expected-value': 'present', - 'value': 'absent' - }) - elif expected_value.state == State.ABSENT: - violations.append({ - 'path': expected_value.path, - 'expected-value': 'absent', - 'value': 'present' - }) - elif expected_value.state == State.SET: - try: - value = self._client.get_value(expected_value.path)['value'] - except NsoException as ex: - if ex.error.get('type', '') == 'data.not_found': - value = None - else: - raise - - # handle different types properly - n_value = normalize_value( - expected_value.value, value, expected_value.path) - if n_value != expected_value.value: - # if the value comparison fails, try mapping identityref - value_type = value_builder.get_type(expected_value.path) - if value_type is not None and 'identityref' in value_type: - n_value, t_value = self.get_prefix_name(value) - - if expected_value.value != n_value: - violations.append({ - 'path': expected_value.path, - 'expected-value': expected_value.value, - 'value': n_value - }) - else: - raise ModuleFailException( - 'value state {0} not supported at {1}'.format( - expected_value.state, expected_value.path)) - - return violations - - -def main(): - argument_spec = dict( - data=dict(required=True, type='dict') - ) - argument_spec.update(nso_argument_spec) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - p = module.params - - client = connect(p) - nso_verify = NsoVerify(client, p['data']) - try: - verify_version(client, NsoVerify.REQUIRED_VERSIONS) - - violations = nso_verify.main() - client.logout() - - num_violations = len(violations) - if num_violations > 0: - msg = '{0} value{1} differ'.format( - num_violations, num_violations > 1 and 's' or '') - module.fail_json(msg=msg, violations=violations) - else: - module.exit_json(changed=False) - - 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/plugins/modules/nso_action.py b/plugins/modules/nso_action.py deleted file mode 120000 index 4349a3be..00000000 --- a/plugins/modules/nso_action.py +++ /dev/null @@ -1 +0,0 @@ -./network/nso/nso_action.py \ No newline at end of file diff --git a/plugins/modules/nso_config.py b/plugins/modules/nso_config.py deleted file mode 120000 index f40cddd6..00000000 --- a/plugins/modules/nso_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/nso/nso_config.py \ No newline at end of file diff --git a/plugins/modules/nso_query.py b/plugins/modules/nso_query.py deleted file mode 120000 index 1f163732..00000000 --- a/plugins/modules/nso_query.py +++ /dev/null @@ -1 +0,0 @@ -./network/nso/nso_query.py \ No newline at end of file diff --git a/plugins/modules/nso_show.py b/plugins/modules/nso_show.py deleted file mode 120000 index 86284271..00000000 --- a/plugins/modules/nso_show.py +++ /dev/null @@ -1 +0,0 @@ -./network/nso/nso_show.py \ No newline at end of file diff --git a/plugins/modules/nso_verify.py b/plugins/modules/nso_verify.py deleted file mode 120000 index e412c964..00000000 --- a/plugins/modules/nso_verify.py +++ /dev/null @@ -1 +0,0 @@ -./network/nso/nso_verify.py \ No newline at end of file diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index ccff6192..2021f510 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -1073,14 +1073,6 @@ plugins/modules/network/nos/nos_config.py validate-modules:parameter-list-no-ele plugins/modules/network/nos/nos_config.py validate-modules:parameter-type-not-in-doc plugins/modules/network/nos/nos_facts.py validate-modules:parameter-list-no-elements plugins/modules/network/nos/nos_facts.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_action.py validate-modules:doc-missing-type -plugins/modules/network/nso/nso_action.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_config.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_config.py validate-modules:return-syntax-error -plugins/modules/network/nso/nso_query.py validate-modules:parameter-list-no-elements -plugins/modules/network/nso/nso_query.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_show.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_verify.py validate-modules:parameter-type-not-in-doc plugins/modules/network/nuage/nuage_vspk.py validate-modules:doc-required-mismatch plugins/modules/network/nuage/nuage_vspk.py validate-modules:missing-suboption-docs plugins/modules/network/nuage/nuage_vspk.py validate-modules:parameter-list-no-elements diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index ccff6192..2021f510 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -1073,14 +1073,6 @@ plugins/modules/network/nos/nos_config.py validate-modules:parameter-list-no-ele plugins/modules/network/nos/nos_config.py validate-modules:parameter-type-not-in-doc plugins/modules/network/nos/nos_facts.py validate-modules:parameter-list-no-elements plugins/modules/network/nos/nos_facts.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_action.py validate-modules:doc-missing-type -plugins/modules/network/nso/nso_action.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_config.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_config.py validate-modules:return-syntax-error -plugins/modules/network/nso/nso_query.py validate-modules:parameter-list-no-elements -plugins/modules/network/nso/nso_query.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_show.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_verify.py validate-modules:parameter-type-not-in-doc plugins/modules/network/nuage/nuage_vspk.py validate-modules:doc-required-mismatch plugins/modules/network/nuage/nuage_vspk.py validate-modules:missing-suboption-docs plugins/modules/network/nuage/nuage_vspk.py validate-modules:parameter-list-no-elements diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index dcf954ba..4419de1d 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -837,12 +837,6 @@ plugins/modules/network/nos/nos_command.py validate-modules:parameter-type-not-i plugins/modules/network/nos/nos_config.py validate-modules:doc-missing-type plugins/modules/network/nos/nos_config.py validate-modules:parameter-type-not-in-doc plugins/modules/network/nos/nos_facts.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_action.py validate-modules:doc-missing-type -plugins/modules/network/nso/nso_action.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_config.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_query.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_show.py validate-modules:parameter-type-not-in-doc -plugins/modules/network/nso/nso_verify.py validate-modules:parameter-type-not-in-doc plugins/modules/network/nuage/nuage_vspk.py validate-modules:missing-suboption-docs plugins/modules/network/nuage/nuage_vspk.py validate-modules:parameter-type-not-in-doc plugins/modules/network/nuage/nuage_vspk.py validate-modules:undocumented-parameter diff --git a/tests/unit/plugins/module_utils/network/nso/__init__.py b/tests/unit/plugins/module_utils/network/nso/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/unit/plugins/module_utils/network/nso/test_nso.py b/tests/unit/plugins/module_utils/network/nso/test_nso.py deleted file mode 100644 index 55a5bc84..00000000 --- a/tests/unit/plugins/module_utils/network/nso/test_nso.py +++ /dev/null @@ -1,660 +0,0 @@ -# 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 - -import json - -from ansible_collections.community.network.tests.unit.compat.mock import patch -from ansible_collections.community.network.tests.unit.compat import unittest -from ansible_collections.community.network.plugins.module_utils.network.nso import nso - - -MODULE_PREFIX_MAP = ''' -{ - "ansible-nso": "an", - "test": "test", - "tailf-ncs": "ncs" -} -''' - - -SCHEMA_DATA = { - '/an:id-name-leaf': ''' -{ - "meta": { - "prefix": "an", - "namespace": "http://github.com/ansible/nso", - "types": { - "http://github.com/ansible/nso:id-name-t": [ - { - "name": "http://github.com/ansible/nso:id-name-t", - "enumeration": [ - { - "label": "id-one" - }, - { - "label": "id-two" - } - ] - }, - { - "name": "identityref" - } - ] - }, - "keypath": "/an:id-name-leaf" - }, - "data": { - "kind": "leaf", - "type": { - "namespace": "http://github.com/ansible/nso", - "name": "id-name-t" - }, - "name": "id-name-leaf", - "qname": "an:id-name-leaf" - } -}''', - '/an:id-name-values': ''' -{ - "meta": { - "prefix": "an", - "namespace": "http://github.com/ansible/nso", - "types": {}, - "keypath": "/an:id-name-values" - }, - "data": { - "kind": "container", - "name": "id-name-values", - "qname": "an:id-name-values", - "children": [ - { - "kind": "list", - "name": "id-name-value", - "qname": "an:id-name-value", - "key": [ - "name" - ] - } - ] - } -} -''', - '/an:id-name-values/id-name-value': ''' -{ - "meta": { - "prefix": "an", - "namespace": "http://github.com/ansible/nso", - "types": { - "http://github.com/ansible/nso:id-name-t": [ - { - "name": "http://github.com/ansible/nso:id-name-t", - "enumeration": [ - { - "label": "id-one" - }, - { - "label": "id-two" - } - ] - }, - { - "name": "identityref" - } - ] - }, - "keypath": "/an:id-name-values/id-name-value" - }, - "data": { - "kind": "list", - "name": "id-name-value", - "qname": "an:id-name-value", - "key": [ - "name" - ], - "children": [ - { - "kind": "key", - "name": "name", - "qname": "an:name", - "type": { - "namespace": "http://github.com/ansible/nso", - "name": "id-name-t" - } - }, - { - "kind": "leaf", - "type": { - "primitive": true, - "name": "string" - }, - "name": "value", - "qname": "an:value" - } - ] - } -} -''', - '/test:test': ''' -{ - "meta": { - "types": { - "http://example.com/test:t15": [ - { - "leaf_type":[ - { - "name":"string" - } - ], - "list_type":[ - { - "name":"http://example.com/test:t15", - "leaf-list":true - } - ] - } - ] - } - }, - "data": { - "kind": "list", - "name":"test", - "qname":"test:test", - "key":["name"], - "children": [ - { - "kind": "key", - "name": "name", - "qname": "test:name", - "type": {"name":"string","primitive":true} - }, - { - "kind": "choice", - "name": "test-choice", - "qname": "test:test-choice", - "cases": [ - { - "kind": "case", - "name": "direct-child-case", - "qname":"test:direct-child-case", - "children":[ - { - "kind": "leaf", - "name": "direct-child", - "qname": "test:direct-child", - "type": {"name":"string","primitive":true} - } - ] - }, - { - "kind":"case","name":"nested-child-case","qname":"test:nested-child-case", - "children": [ - { - "kind": "choice", - "name": "nested-choice", - "qname": "test:nested-choice", - "cases": [ - { - "kind":"case","name":"nested-child","qname":"test:nested-child", - "children": [ - { - "kind": "leaf", - "name":"nested-child", - "qname":"test:nested-child", - "type":{"name":"string","primitive":true}} - ] - } - ] - } - ] - } - ] - }, - { - "kind":"leaf-list", - "name":"device-list", - "qname":"test:device-list", - "type": { - "namespace":"http://example.com/test", - "name":"t15" - } - } - ] - } -} -''', - '/test:test/device-list': ''' -{ - "meta": { - "types": { - "http://example.com/test:t15": [ - { - "leaf_type":[ - { - "name":"string" - } - ], - "list_type":[ - { - "name":"http://example.com/test:t15", - "leaf-list":true - } - ] - } - ] - } - }, - "data": { - "kind":"leaf-list", - "name":"device-list", - "qname":"test:device-list", - "type": { - "namespace":"http://example.com/test", - "name":"t15" - } - } -} -''', - '/test:deps': ''' -{ - "meta": { - }, - "data": { - "kind":"container", - "name":"deps", - "qname":"test:deps", - "children": [ - { - "kind": "leaf", - "type": { - "primitive": true, - "name": "string" - }, - "name": "a", - "qname": "test:a", - "deps": ["/test:deps/c"] - }, - { - "kind": "leaf", - "type": { - "primitive": true, - "name": "string" - }, - "name": "b", - "qname": "test:b", - "deps": ["/test:deps/a"] - }, - { - "kind": "leaf", - "type": { - "primitive": true, - "name": "string" - }, - "name": "c", - "qname": "test:c" - } - ] - } -} -''' -} - - -class MockResponse(object): - def __init__(self, method, params, code, body, headers=None): - if headers is None: - headers = {} - - self.method = method - self.params = params - - self.code = code - self.body = body - self.headers = dict(headers) - - def read(self): - return self.body - - -def mock_call(calls, url, timeout, validate_certs, data=None, headers=None, method=None): - result = calls[0] - del calls[0] - - request = json.loads(data) - if result.method != request['method']: - raise ValueError('expected method {0}({1}), got {2}({3})'.format( - result.method, result.params, - request['method'], request['params'])) - - for key, value in result.params.items(): - if key not in request['params']: - raise ValueError('{0} not in parameters'.format(key)) - if value != request['params'][key]: - raise ValueError('expected {0} to be {1}, got {2}'.format( - key, value, request['params'][key])) - - return result - - -def get_schema_response(path): - return MockResponse( - 'get_schema', {'path': path}, 200, '{{"result": {0}}}'.format( - SCHEMA_DATA[path])) - - -class TestJsonRpc(unittest.TestCase): - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_exists(self, open_url_mock): - calls = [ - MockResponse('new_trans', {}, 200, '{"result": {"th": 1}}'), - MockResponse('exists', {'path': '/exists'}, 200, '{"result": {"exists": true}}'), - MockResponse('exists', {'path': '/not-exists'}, 200, '{"result": {"exists": false}}') - ] - open_url_mock.side_effect = lambda *args, **kwargs: mock_call(calls, *args, **kwargs) - client = nso.JsonRpc('http://localhost:8080/jsonrpc', 10, False) - self.assertEqual(True, client.exists('/exists')) - self.assertEqual(False, client.exists('/not-exists')) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_exists_data_not_found(self, open_url_mock): - calls = [ - MockResponse('new_trans', {}, 200, '{"result": {"th": 1}}'), - MockResponse('exists', {'path': '/list{missing-parent}/list{child}'}, 200, '{"error":{"type":"data.not_found"}}') - ] - open_url_mock.side_effect = lambda *args, **kwargs: mock_call(calls, *args, **kwargs) - client = nso.JsonRpc('http://localhost:8080/jsonrpc', 10, False) - self.assertEqual(False, client.exists('/list{missing-parent}/list{child}')) - - self.assertEqual(0, len(calls)) - - -class TestValueBuilder(unittest.TestCase): - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_identityref_leaf(self, open_url_mock): - calls = [ - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5"}'), - MockResponse('new_trans', {}, 200, '{"result": {"th": 1}}'), - get_schema_response('/an:id-name-leaf'), - MockResponse('get_module_prefix_map', {}, 200, '{{"result": {0}}}'.format(MODULE_PREFIX_MAP)) - ] - open_url_mock.side_effect = lambda *args, **kwargs: mock_call(calls, *args, **kwargs) - - parent = "/an:id-name-leaf" - schema_data = json.loads( - SCHEMA_DATA['/an:id-name-leaf']) - schema = schema_data['data'] - - vb = nso.ValueBuilder(nso.JsonRpc('http://localhost:8080/jsonrpc', 10, False)) - vb.build(parent, None, 'ansible-nso:id-two', schema) - values = list(vb.values) - self.assertEqual(1, len(values)) - value = values[0] - self.assertEqual(parent, value.path) - self.assertEqual('set', value.state) - self.assertEqual('an:id-two', value.value) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_identityref_key(self, open_url_mock): - calls = [ - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5"}'), - MockResponse('new_trans', {}, 200, '{"result": {"th": 1}}'), - get_schema_response('/an:id-name-values/id-name-value'), - MockResponse('get_module_prefix_map', {}, 200, '{{"result": {0}}}'.format(MODULE_PREFIX_MAP)), - MockResponse('exists', {'path': '/an:id-name-values/id-name-value{an:id-one}'}, 200, '{"result": {"exists": true}}') - ] - open_url_mock.side_effect = lambda *args, **kwargs: mock_call(calls, *args, **kwargs) - - parent = "/an:id-name-values" - schema_data = json.loads( - SCHEMA_DATA['/an:id-name-values/id-name-value']) - schema = schema_data['data'] - - vb = nso.ValueBuilder(nso.JsonRpc('http://localhost:8080/jsonrpc', 10, False)) - vb.build(parent, 'id-name-value', [{'name': 'ansible-nso:id-one', 'value': '1'}], schema) - values = list(vb.values) - self.assertEqual(1, len(values)) - value = values[0] - self.assertEqual('{0}/id-name-value{{an:id-one}}/value'.format(parent), value.path) - self.assertEqual('set', value.state) - self.assertEqual('1', value.value) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nested_choice(self, open_url_mock): - calls = [ - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5"}'), - MockResponse('new_trans', {}, 200, '{"result": {"th": 1}}'), - get_schema_response('/test:test'), - MockResponse('exists', {'path': '/test:test{direct}'}, 200, '{"result": {"exists": true}}'), - MockResponse('exists', {'path': '/test:test{nested}'}, 200, '{"result": {"exists": true}}') - ] - open_url_mock.side_effect = lambda *args, **kwargs: mock_call(calls, *args, **kwargs) - - parent = "/test:test" - schema_data = json.loads( - SCHEMA_DATA['/test:test']) - schema = schema_data['data'] - - vb = nso.ValueBuilder(nso.JsonRpc('http://localhost:8080/jsonrpc', 10, False)) - vb.build(parent, None, [{'name': 'direct', 'direct-child': 'direct-value'}, - {'name': 'nested', 'nested-child': 'nested-value'}], schema) - values = list(vb.values) - self.assertEqual(2, len(values)) - value = values[0] - self.assertEqual('{0}{{direct}}/direct-child'.format(parent), value.path) - self.assertEqual('set', value.state) - self.assertEqual('direct-value', value.value) - - value = values[1] - self.assertEqual('{0}{{nested}}/nested-child'.format(parent), value.path) - self.assertEqual('set', value.state) - self.assertEqual('nested-value', value.value) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_leaf_list_type(self, open_url_mock): - calls = [ - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.4"}'), - MockResponse('new_trans', {}, 200, '{"result": {"th": 1}}'), - get_schema_response('/test:test') - ] - open_url_mock.side_effect = lambda *args, **kwargs: mock_call(calls, *args, **kwargs) - - parent = "/test:test" - schema_data = json.loads( - SCHEMA_DATA['/test:test']) - schema = schema_data['data'] - - vb = nso.ValueBuilder(nso.JsonRpc('http://localhost:8080/jsonrpc', 10, False)) - vb.build(parent, None, {'device-list': ['one', 'two']}, schema) - values = list(vb.values) - self.assertEqual(1, len(values)) - value = values[0] - self.assertEqual('{0}/device-list'.format(parent), value.path) - self.assertEqual(['one', 'two'], value.value) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_leaf_list_type_45(self, open_url_mock): - calls = [ - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5"}'), - MockResponse('new_trans', {}, 200, '{"result": {"th": 1}}'), - get_schema_response('/test:test/device-list') - ] - open_url_mock.side_effect = lambda *args, **kwargs: mock_call(calls, *args, **kwargs) - - parent = "/test:test" - schema_data = json.loads( - SCHEMA_DATA['/test:test']) - schema = schema_data['data'] - - vb = nso.ValueBuilder(nso.JsonRpc('http://localhost:8080/jsonrpc', 10, False)) - vb.build(parent, None, {'device-list': ['one', 'two']}, schema) - values = list(vb.values) - self.assertEqual(3, len(values)) - value = values[0] - self.assertEqual('{0}/device-list'.format(parent), value.path) - self.assertEqual(nso.State.ABSENT, value.state) - value = values[1] - self.assertEqual('{0}/device-list{{one}}'.format(parent), value.path) - self.assertEqual(nso.State.PRESENT, value.state) - value = values[2] - self.assertEqual('{0}/device-list{{two}}'.format(parent), value.path) - self.assertEqual(nso.State.PRESENT, value.state) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_sort_by_deps(self, open_url_mock): - calls = [ - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5"}'), - MockResponse('new_trans', {}, 200, '{"result": {"th": 1}}'), - get_schema_response('/test:deps') - ] - open_url_mock.side_effect = lambda *args, **kwargs: mock_call(calls, *args, **kwargs) - - parent = "/test:deps" - schema_data = json.loads( - SCHEMA_DATA['/test:deps']) - schema = schema_data['data'] - - values = { - 'a': '1', - 'b': '2', - 'c': '3', - } - - vb = nso.ValueBuilder(nso.JsonRpc('http://localhost:8080/jsonrpc', 10, False)) - vb.build(parent, None, values, schema) - values = list(vb.values) - self.assertEqual(3, len(values)) - value = values[0] - self.assertEqual('{0}/c'.format(parent), value.path) - self.assertEqual('3', value.value) - value = values[1] - self.assertEqual('{0}/a'.format(parent), value.path) - self.assertEqual('1', value.value) - value = values[2] - self.assertEqual('{0}/b'.format(parent), value.path) - self.assertEqual('2', value.value) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_sort_by_deps_not_included(self, open_url_mock): - calls = [ - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5"}'), - MockResponse('new_trans', {}, 200, '{"result": {"th": 1}}'), - get_schema_response('/test:deps') - ] - open_url_mock.side_effect = lambda *args, **kwargs: mock_call(calls, *args, **kwargs) - - parent = "/test:deps" - schema_data = json.loads( - SCHEMA_DATA['/test:deps']) - schema = schema_data['data'] - - values = { - 'a': '1', - 'b': '2' - } - - vb = nso.ValueBuilder(nso.JsonRpc('http://localhost:8080/jsonrpc', 10, False)) - vb.build(parent, None, values, schema) - values = list(vb.values) - self.assertEqual(2, len(values)) - value = values[0] - self.assertEqual('{0}/a'.format(parent), value.path) - self.assertEqual('1', value.value) - value = values[1] - self.assertEqual('{0}/b'.format(parent), value.path) - self.assertEqual('2', 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)])) - - -class TestValueSort(unittest.TestCase): - def test_sort_parent_depend(self): - values = [ - nso.ValueBuilder.Value('/test/list{entry}', '/test/list', 'CREATE', ['']), - nso.ValueBuilder.Value('/test/list{entry}/description', '/test/list/description', 'TEST', ['']), - nso.ValueBuilder.Value('/test/entry', '/test/entry', 'VALUE', ['/test/list', '/test/list/name']) - ] - - result = [v.path for v in nso.ValueBuilder.sort_values(values)] - - self.assertEqual(['/test/list{entry}', '/test/entry', '/test/list{entry}/description'], result) - - def test_sort_break_direct_cycle(self): - values = [ - nso.ValueBuilder.Value('/test/a', '/test/a', 'VALUE', ['/test/c']), - nso.ValueBuilder.Value('/test/b', '/test/b', 'VALUE', ['/test/a']), - nso.ValueBuilder.Value('/test/c', '/test/c', 'VALUE', ['/test/a']) - ] - - result = [v.path for v in nso.ValueBuilder.sort_values(values)] - - self.assertEqual(['/test/a', '/test/b', '/test/c'], result) - - def test_sort_break_indirect_cycle(self): - values = [ - nso.ValueBuilder.Value('/test/c', '/test/c', 'VALUE', ['/test/a']), - nso.ValueBuilder.Value('/test/a', '/test/a', 'VALUE', ['/test/b']), - nso.ValueBuilder.Value('/test/b', '/test/b', 'VALUE', ['/test/c']) - ] - - result = [v.path for v in nso.ValueBuilder.sort_values(values)] - - self.assertEqual(['/test/a', '/test/c', '/test/b'], result) - - def test_sort_depend_on_self(self): - values = [ - nso.ValueBuilder.Value('/test/a', '/test/a', 'VALUE', ['/test/a']), - nso.ValueBuilder.Value('/test/b', '/test/b', 'VALUE', []) - ] - - result = [v.path for v in nso.ValueBuilder.sort_values(values)] - - self.assertEqual(['/test/a', '/test/b'], result) diff --git a/tests/unit/plugins/modules/network/nso/__init__.py b/tests/unit/plugins/modules/network/nso/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/unit/plugins/modules/network/nso/fixtures/complex_schema.json b/tests/unit/plugins/modules/network/nso/fixtures/complex_schema.json deleted file mode 100644 index 49a11765..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/complex_schema.json +++ /dev/null @@ -1,212 +0,0 @@ -{ - "meta": { - "prefix": "ansible", - "namespace": "http://example.com/ansible", - "types": { - }, - "keypath": "/ansible:action/complex" - }, - "data": { - "kind": "action", - "mandatory": true, - "name": "complex", - "qname": "ansible:complex", - "access": { - "read": false, - "create": false, - "execute": true, - "update": false, - "delete": false - }, - "children": [ - { - "kind": "leaf", - "is_action_input": true, - "name": "number", - "qname": "ansible:number", - "access": { - "read": false, - "create": false, - "execute": false, - "update": true, - "delete": false - }, - "type": { - "primitive": true, - "name": "uint8" - } - }, - { - "kind": "container", - "is_action_input": true, - "mandatory": true, - "name": "ansible", - "qname": "ansible:ansible", - "access": { - "read": false, - "create": false, - "execute": false, - "update": true, - "delete": false - }, - "children": [ - { - "kind": "choice", - "cases": [ - { - "kind": "case", - "name": "version", - "children": [ - { - "kind": "leaf", - "is_action_input": true, - "name": "version", - "qname": "ansible:version", - "access": { - "read": false, - "create": false, - "execute": false, - "update": true, - "delete": false - }, - "type": { - "primitive": true, - "name": "string" - } - } - ] - }, - { - "kind": "case", - "name": "release", - "children": [ - { - "kind": "container", - "is_action_input": true, - "mandatory": true, - "name": "release", - "qname": "ansible:release", - "access": { - "read": false, - "create": false, - "execute": false, - "update": true, - "delete": false - }, - "children": [ - { - "kind": "leaf", - "is_action_input": true, - "name": "major", - "qname": "ansible:major", - "access": { - "read": false, - "create": false, - "execute": false, - "update": true, - "delete": false - }, - "type": { - "primitive": true, - "name": "uint8" - } - }, - { - "kind": "leaf", - "is_action_input": true, - "name": "minor", - "qname": "ansible:minor", - "access": { - "read": false, - "create": false, - "execute": false, - "update": true, - "delete": false - }, - "type": { - "primitive": true, - "name": "uint8" - } - } - ] - } - ] - } - ], - "name": "version-releae-choice" - } - ] - }, - { - "kind": "choice", - "cases": [ - { - "kind": "case", - "name": "version", - "children": [ - { - "kind": "list", - "min_elements": 0, - "name": "version", - "max_elements": "unbounded", - "qname": "ansible:version", - "access": { - "read": false, - "create": false, - "execute": false, - "update": false, - "delete": false - }, - "mandatory": true, - "children": [ - { - "kind": "leaf", - "name": "name", - "qname": "ansible:name", - "access": { - "read": false, - "create": false, - "execute": false, - "update": false, - "delete": false - }, - "type": { - "primitive": true, - "name": "string" - }, - "is_action_output": true - } - ], - "is_action_output": true - } - ] - }, - { - "kind": "case", - "name": "release", - "children": [ - { - "kind": "leaf", - "name": "release", - "qname": "ansible:release", - "access": { - "read": false, - "create": false, - "execute": false, - "update": false, - "delete": false - }, - "type": { - "primitive": true, - "name": "string" - }, - "is_action_output": true - } - ] - } - ], - "name": "version-release-choice" - } - ] - } -} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/config_config.json b/tests/unit/plugins/modules/network/nso/fixtures/config_config.json deleted file mode 100644 index b7318586..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/config_config.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "l3vpn:vpn": { - "l3vpn": [ - { - "name": "company", - "route-distinguisher": 999, - "endpoint": [ - { - "id": "branch-office1", - "ce-device": "ce6", - "ce-interface": "GigabitEthernet0/12", - "ip-network": "10.10.1.0/24", - "bandwidth": 12000000, - "as-number": 65101 - } - ] - } - ] - } -} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/config_config_changes.json b/tests/unit/plugins/modules/network/nso/fixtures/config_config_changes.json deleted file mode 100644 index 3ef234b7..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/config_config_changes.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "changes": [ - { - "path": "/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/ce-device", - "old": "", - "value": "ce6", - "op": "value_set" - }, - { - "path": "/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/ip-network", - "old": "", - "value": "10.10.1.0/24", - "op": "value_set" - }, - { - "path": "/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/as-number", - "old": "", - "value": "65101", - "op": "value_set" - }, - { - "path": "/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/ce-interface", - "old": "", - "value": "GigabitEthernet0/12", - "op": "value_set" - }, - { - "path": "/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/bandwidth", - "old": "", - "value": "12000000", - "op": "value_set" - }, - { - "path": "/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}", - "old": "", - "value": "", - "op": "created" - }, - { - "path": "/l3vpn:vpn/l3vpn{company}", - "old": "", - "value": "", - "op": "modified" - } - ] -} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/config_empty_data.json b/tests/unit/plugins/modules/network/nso/fixtures/config_empty_data.json deleted file mode 100644 index 0967ef42..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/config_empty_data.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/description_schema.json b/tests/unit/plugins/modules/network/nso/fixtures/description_schema.json deleted file mode 100644 index 2680a484..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/description_schema.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "meta": { - "prefix": "ncs", - "namespace": "http://tail-f.com/ns/ncs", - "types": { - }, - "keypath": "/ncs:devices/device{ce0}/description" - }, - "data": { - "info": { - "string": "Free form textual description" - }, - "kind": "leaf", - "name": "description", - "qname": "ncs:description", - "access": { - "read": true, - "create": true, - "execute": false, - "update": true, - "delete": true - }, - "type": { - "primitive": true, - "name": "string" - } - } -} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/device_schema.json b/tests/unit/plugins/modules/network/nso/fixtures/device_schema.json deleted file mode 100644 index d3bd2ac3..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/device_schema.json +++ /dev/null @@ -1 +0,0 @@ -{"meta": {"prefix": "ncs", "namespace": "http://tail-f.com/ns/ncs", "types": {"http://tail-f.com/ns/ncs:t85": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t85"}], "leaf_type": [{"name": "string"}]}], "urn:ietf:params:xml:ns:yang:ietf-inet-types:port-number": [{"range": {"value": [["0", "65535"]]}, "name": "urn:ietf:params:xml:ns:yang:ietf-inet-types:port-number"}, {"name": "uint16"}], "http://tail-f.com/ns/ncs:t83": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t83"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:node-name": [{"name": "http://tail-f.com/ns/ncs:node-name"}, {"max-length": {"value": 253}, "min-length": {"value": 1}, "name": "urn:ietf:params:xml:ns:yang:ietf-inet-types:domain-name", "pattern": {"value": "((([a-zA-Z0-9_]([a-zA-Z0-9\\-_]){0,61})?[a-zA-Z0-9]\\.)*([a-zA-Z0-9_]([a-zA-Z0-9\\-_]){0,61})?[a-zA-Z0-9]\\.?)|\\."}}, {"name": "string"}], "http://tail-f.com/ns/ncs:t29": [{"range": {"value": [["1", "4294967"]]}, "name": "http://tail-f.com/ns/ncs:t29"}, {"name": "uint32"}], "http://tail-f.com/ns/ncs:t101": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t101"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t43": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t43"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t27": [{"range": {"value": [["1", "4294967"]]}, "name": "http://tail-f.com/ns/ncs:t27"}, {"name": "uint32"}], "http://tail-f.com/ns/ncs:t40": [{"name": "http://tail-f.com/ns/ncs:t40", "enumeration": [{"label": "reject"}, {"label": "accept"}]}, {"name": "string"}], "http://tail-f.com/ns/ncs:t47": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t47"}], "leaf_type": [{"name": "string"}]}], "urn:ietf:params:xml:ns:yang:ietf-inet-types:host": [{"union": [[{"name": "urn:ietf:params:xml:ns:yang:ietf-inet-types:ip-address"}, {"name": "ip-address"}], [{"max-length": {"value": 253}, "min-length": {"value": 1}, "name": "urn:ietf:params:xml:ns:yang:ietf-inet-types:domain-name", "pattern": {"value": "((([a-zA-Z0-9_]([a-zA-Z0-9\\-_]){0,61})?[a-zA-Z0-9]\\.)*([a-zA-Z0-9_]([a-zA-Z0-9\\-_]){0,61})?[a-zA-Z0-9]\\.?)|\\."}}, {"name": "string"}]]}], "http://tail-f.com/ns/ncs:t45": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t45"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t49": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t49"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:trace-flag": [{"name": "http://tail-f.com/ns/ncs:trace-flag", "enumeration": [{"info": "Trace is disabled", "label": "false"}, {"info": "Raw, unformatted data", "label": "raw"}, {"info": "Pretty-printed data", "label": "pretty"}]}, {"name": "string"}], "http://tail-f.com/ns/ncs:t28": [{"range": {"value": [["1", "4294967"]]}, "name": "http://tail-f.com/ns/ncs:t28"}, {"name": "uint32"}]}, "keypath": "/ncs:devices/device"}, "data": {"info": {"string": "The list of managed devices"}, "kind": "list", "leafref_groups": [["remote-node"], ["authgroup"], ["device-profile"]], "mandatory": true, "name": "device", "max_elements": "unbounded", "contains_when_statement": true, "qname": "ncs:device", "children": [{"info": {"string": "A string uniquely identifying the managed device"}, "kind": "key", "mandatory": true, "name": "name", "qname": "ncs:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "IP address or host name for the management interface"}, "kind": "leaf", "name": "address", "qname": "ncs:address", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "host"}}, {"info": {"string": "Port for the management interface"}, "kind": "leaf", "name": "port", "qname": "ncs:port", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "port-number"}}, {"info": {"string": "Name of remote node which connects to device"}, "kind": "leaf", "name": "remote-node", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "node-name"}, "qname": "ncs:remote-node", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:cluster/remote-node/name", "is_leafref": true}, {"info": {"string": "SSH connection configuration"}, "kind": "container", "mandatory": true, "name": "ssh", "qname": "ncs:ssh", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Free form textual description"}, "kind": "leaf", "name": "description", "qname": "ncs:description", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "Physical location of devices in the group"}, "kind": "container", "mandatory": true, "name": "location", "qname": "ncs:location", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Authentication credentials for the device"}, "kind": "leaf", "name": "authgroup", "type": {"primitive": true, "name": "string"}, "qname": "ncs:authgroup", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:devices/authgroups/group/name", "is_leafref": true}, {"info": {"string": "Management protocol for the device"}, "kind": "container", "mandatory": true, "name": "device-type", "qname": "ncs:device-type", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"kind": "leaf", "name": "device-profile", "type": {"primitive": true, "name": "string"}, "qname": "ncs:device-profile", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:devices/profiles/profile/name", "is_leafref": true}, {"info": {"string": "Timeout in seconds for new connections"}, "kind": "leaf", "name": "connect-timeout", "qname": "ncs:connect-timeout", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "units": "seconds", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t27"}}, {"info": {"string": "Timeout in seconds used when reading data"}, "kind": "leaf", "name": "read-timeout", "qname": "ncs:read-timeout", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "units": "seconds", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t28"}}, {"info": {"string": "Timeout in seconds used when writing data"}, "kind": "leaf", "name": "write-timeout", "qname": "ncs:write-timeout", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "units": "seconds", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t29"}}, {"info": {"string": "Controls SSH keep alive settings"}, "kind": "container", "mandatory": true, "name": "ssh-keep-alive", "qname": "ncs:ssh-keep-alive", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Trace the southbound communication to devices"}, "kind": "leaf", "name": "trace", "qname": "ncs:trace", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "trace-flag"}}, {"info": {"string": "Control which device capabilities NCS uses"}, "kind": "container", "mandatory": true, "name": "ned-settings", "qname": "ncs:ned-settings", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Control settings for the commit-queue"}, "kind": "container", "mandatory": true, "name": "commit-queue", "qname": "ncs:commit-queue", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Control how sessions to related devices can be pooled."}, "kind": "container", "mandatory": true, "name": "session-pool", "qname": "ncs:session-pool", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Control settings for no-overwrite sync check"}, "kind": "container", "mandatory": true, "name": "no-overwrite", "qname": "ncs:no-overwrite", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Specifies the behaviour of a commit operation involving a\ndevice that is out of sync with NCS. Value accept assumes that\nthe device's sync state is unknown and it is cleared on commit.\nThe default behaviour is to reject such commits."}, "kind": "leaf", "name": "out-of-sync-commit-behaviour", "qname": "ncs:out-of-sync-commit-behaviour", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t40"}}, {"default": "use-lsa", "kind": "choice", "cases": [{"kind": "case", "name": "use-lsa", "children": [{"info": {"string": "Handle the LSA nodes as such. This is the default"}, "kind": "leaf", "name": "use-lsa", "qname": "ncs:use-lsa", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "no-lsa", "children": [{"info": {"string": "Do not handle any of the LSA nodes as such. These nodes\nwill be handled as any other device. This has the same\nresult as adding the commit flag 'no-lsa' to every commit."}, "kind": "leaf", "name": "no-lsa", "qname": "ncs:no-lsa", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"primitive": true, "name": "empty"}}]}], "name": "choice-lsa"}, {"info": {"string": "Show all active settings for the device"}, "kind": "container", "mandatory": true, "name": "active-settings", "qname": "ncs:active-settings", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "Additional protocols for the live-tree (read-only)"}, "kind": "list", "leafref_groups": [["authgroup"]], "min_elements": 0, "name": "live-status-protocol", "max_elements": "unbounded", "qname": "ncs:live-status-protocol", "children": [{"kind": "key", "mandatory": true, "name": "name", "qname": "ncs:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "IP Address for the management interface"}, "kind": "leaf", "name": "address", "qname": "ncs:address", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "host"}}, {"info": {"string": "Port for the management interface"}, "kind": "leaf", "name": "port", "qname": "ncs:port", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "port-number"}}, {"info": {"string": "SSH host key configuration"}, "kind": "container", "name": "ssh", "presence": true, "qname": "ncs:ssh", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}}, {"info": {"string": "Authentication credentials for the device"}, "kind": "leaf", "name": "authgroup", "type": {"primitive": true, "name": "string"}, "qname": "ncs:authgroup", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:devices/authgroups/group/name", "is_leafref": true}, {"info": {"string": "Management protocol for the device"}, "kind": "container", "mandatory": true, "name": "device-type", "qname": "ncs:device-type", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Operational State for the live protocol"}, "kind": "container", "mandatory": true, "name": "state", "qname": "ncs:state", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "List of capabillities supported by the device"}, "kind": "list", "min_elements": 0, "name": "capability", "max_elements": "unbounded", "qname": "ncs:capability", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "key": ["uri"], "mandatory": true, "config": false, "children": [{"info": {"string": "Capability URI"}, "kind": "key", "mandatory": true, "name": "uri", "type": {"primitive": true, "name": "string"}, "qname": "ncs:uri", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "Capability revision"}, "kind": "leaf", "name": "revision", "type": {"primitive": true, "name": "string"}, "qname": "ncs:revision", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Capability module"}, "kind": "leaf", "name": "module", "type": {"primitive": true, "name": "string"}, "qname": "ncs:module", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Capability features"}, "kind": "leaf-list", "name": "feature", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t83"}, "qname": "ncs:feature", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Capability deviations"}, "kind": "leaf-list", "name": "deviation", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t85"}, "qname": "ncs:deviation", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}]}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["name"], "mandatory": true, "leafrefGroups": [["authgroup"]]}, {"info": {"string": "Show states for the device"}, "kind": "container", "mandatory": true, "name": "state", "qname": "ncs:state", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "How the device was added to NCS"}, "kind": "container", "mandatory": true, "name": "source", "qname": "ncs:source", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "A list of capabilities supported by the device"}, "kind": "list", "min_elements": 0, "name": "capability", "max_elements": "unbounded", "qname": "ncs:capability", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "key": ["uri"], "mandatory": true, "config": false, "children": [{"kind": "key", "mandatory": true, "name": "uri", "type": {"primitive": true, "name": "string"}, "qname": "ncs:uri", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"kind": "leaf", "name": "revision", "config": false, "qname": "ncs:revision", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf", "name": "module", "config": false, "qname": "ncs:module", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf-list", "name": "feature", "config": false, "qname": "ncs:feature", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t43"}}, {"kind": "leaf-list", "name": "deviation", "config": false, "qname": "ncs:deviation", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t45"}}, {"info": {"string": "This action removes a capability from the list of capabilities.\nIf leaf module is set then corresponding module is attempted to\nbe removed from the list of modules for this device. This action\nis only intended to be used for pre-provisioning: it is not\npossible to override capabilities and modules provided by the\nNED implementation using this action."}, "kind": "action", "mandatory": true, "name": "remove", "qname": "ncs:remove", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}]}, {"info": {"string": "This is a list of the YANG modules supported by the device.\n\nThis list is populated the first time NCS connects to the\ndevice."}, "kind": "list", "min_elements": 0, "name": "module", "max_elements": "unbounded", "qname": "ncs:module", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "key": ["name"], "mandatory": true, "config": false, "children": [{"kind": "key", "mandatory": true, "name": "name", "type": {"primitive": true, "name": "string"}, "qname": "ncs:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"kind": "leaf", "name": "revision", "config": false, "qname": "ncs:revision", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf-list", "name": "feature", "config": false, "qname": "ncs:feature", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t47"}}, {"kind": "leaf-list", "name": "deviation", "config": false, "qname": "ncs:deviation", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t49"}}]}, {"info": {"string": "Contains vendor-specific information for\nidentifying the system platform.\n\nNEDs MAY augment this container with more device-specific\nnodes."}, "kind": "container", "mandatory": true, "name": "platform", "qname": "ncs:platform", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "NCS copy of the device configuration"}, "kind": "container", "mandatory": true, "name": "config", "qname": "ncs:config", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Status data fetched from the device"}, "kind": "container", "mandatory": true, "name": "live-status", "qname": "ncs:live-status", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "RPCs from the device"}, "kind": "container", "mandatory": true, "name": "rpc", "qname": "ncs:rpc", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "NETCONF notifications from the device"}, "kind": "container", "mandatory": true, "name": "netconf-notifications", "qname": "ncs:netconf-notifications", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Show services that use this device"}, "kind": "leaf-list", "name": "service-list", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t101"}, "qname": "ncs:service-list", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Notification address if different from device address"}, "kind": "leaf", "name": "snmp-notification-address", "qname": "ncs:snmp-notification-address", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "host"}}, {"info": {"string": "Device specific information"}, "kind": "container", "name": "platform", "presence": true, "when_targets": ["/ncs:devices/device/device-type/cli/ned-id"], "qname": "alu-meta:platform", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}}, {"info": {"string": "A summary of all active alarms per device."}, "kind": "container", "mandatory": true, "name": "alarm-summary", "qname": "al:alarm-summary", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "Note: this action overwrites existing list of capabilities.\n\nThis action copies the list of capabilities and the list of modules\nfrom another device or profile. When used on a device, this action\nis only intended to be used for pre-provisioning: it is not possible\nto override capabilities and modules provided by the\nNED implementation using this action."}, "kind": "action", "mandatory": true, "name": "copy-capabilities", "qname": "ncs:copy-capabilities", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Note: this action overwrites existing list of capabilities.\n\nThis action populates the list of capabilities based on the\nconfigured ned-id for this device, if possible. NCS will look up\nthe package corresponding to the ned-id and add all the modules\nfrom this packages to the list of this device's capabilities and\nlist of modules. It is the responsibility of the caller to verify\nthat the automatically populated list of capabilities matches actual\ndevice's capabilities. The list of capabilities can then be\nfine-tuned using add-capability and capability/remove actions.\nCurrently this approach will only work for CLI and generic devices.\nThis action is only intended to be used for pre-provisioning:\nit is not possible to override capabilities and modules provided\nby the NED implementation using this action."}, "kind": "action", "mandatory": true, "name": "find-capabilities", "qname": "ncs:find-capabilities", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "This action adds a capability to the list of capabilities.\nIf uri is specified, then it is parsed as YANG capability string\nand module, revision, feature and deviation parameters are derived\nfrom the string. If module is specified, then the namespace is\nlooked up in the list of loaded namespaces and capability string\nconstructed automatically. If the module is specified and the\nattempt to look it up failed, then the action does nothing.\nIf module is specified or can be derived from capability string,\nthen the module is also added/replaced in the list of modules. This\naction is only intended to be used for pre-provisioning: it is not\npossible to override capabilities and modules provided by the NED\nimplementation using this action."}, "kind": "action", "mandatory": true, "name": "add-capability", "qname": "ncs:add-capability", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Take a named template and copy it here"}, "kind": "action", "mandatory": true, "name": "apply-template", "leafrefGroups": [["template-name"]], "qname": "ncs:apply-template", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["template-name"]]}, {"info": {"string": "Instantiate the config for the device from existing device"}, "kind": "action", "mandatory": true, "name": "instantiate-from-other-device", "leafrefGroups": [["device-name"]], "qname": "ncs:instantiate-from-other-device", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["device-name"]]}, {"info": {"string": "Compare the actual device config with the NCS copy"}, "kind": "action", "mandatory": true, "name": "compare-config", "qname": "ncs:compare-config", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Synchronize the config by pulling from the device"}, "kind": "action", "mandatory": true, "name": "sync-from", "qname": "ncs:sync-from", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Synchronize the config by pushing to the device"}, "kind": "action", "mandatory": true, "name": "sync-to", "qname": "ncs:sync-to", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Check if the NCS config is in sync with the device"}, "kind": "action", "mandatory": true, "name": "check-sync", "qname": "ncs:check-sync", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Check if NCS and the device have compatible YANG modules"}, "kind": "action", "mandatory": true, "name": "check-yang-modules", "qname": "ncs:check-yang-modules", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Connect to the device"}, "kind": "action", "mandatory": true, "name": "connect", "qname": "ncs:connect", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Close all sessions to the device"}, "kind": "action", "mandatory": true, "name": "disconnect", "qname": "ncs:disconnect", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "ICMP ping the device"}, "kind": "action", "mandatory": true, "name": "ping", "qname": "ncs:ping", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Delete the config in NCS without deleting it in the device"}, "kind": "action", "mandatory": true, "name": "delete-config", "qname": "ncs:delete-config", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Secure copy file to the device"}, "kind": "action", "mandatory": true, "name": "scp-to", "qname": "ncs:scp-to", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Secure copy file to the device"}, "kind": "action", "mandatory": true, "name": "scp-from", "qname": "ncs:scp-from", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["name"], "min_elements": 0, "leafrefGroups": [["remote-node"], ["authgroup"], ["device-profile"]]}} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/devices_schema.json b/tests/unit/plugins/modules/network/nso/fixtures/devices_schema.json deleted file mode 100644 index 541ba010..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/devices_schema.json +++ /dev/null @@ -1 +0,0 @@ -{"meta": {"prefix": "ncs", "namespace": "http://tail-f.com/ns/ncs", "types": {"http://tail-f.com/ns/ncs:t68": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t68"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t85": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t85"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t83": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t83"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t60": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t60"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t101": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t101"}], "leaf_type": [{"name": "string"}]}], "urn:ietf:params:xml:ns:yang:ietf-inet-types:host": [{"union": [[{"name": "urn:ietf:params:xml:ns:yang:ietf-inet-types:ip-address"}, {"name": "ip-address"}], [{"max-length": {"value": 253}, "min-length": {"value": 1}, "name": "urn:ietf:params:xml:ns:yang:ietf-inet-types:domain-name", "pattern": {"value": "((([a-zA-Z0-9_]([a-zA-Z0-9\\-_]){0,61})?[a-zA-Z0-9]\\.)*([a-zA-Z0-9_]([a-zA-Z0-9\\-_]){0,61})?[a-zA-Z0-9]\\.?)|\\."}}, {"name": "string"}]]}], "http://tail-f.com/ns/ncs:trace-flag": [{"name": "http://tail-f.com/ns/ncs:trace-flag", "enumeration": [{"info": "Trace is disabled", "label": "false"}, {"info": "Raw, unformatted data", "label": "raw"}, {"info": "Pretty-printed data", "label": "pretty"}]}, {"name": "string"}], "http://tail-f.com/ns/ncs:t43": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t43"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t27": [{"range": {"value": [["1", "4294967"]]}, "name": "http://tail-f.com/ns/ncs:t27"}, {"name": "uint32"}], "http://tail-f.com/ns/ncs:t40": [{"name": "http://tail-f.com/ns/ncs:t40", "enumeration": [{"label": "reject"}, {"label": "accept"}]}, {"name": "string"}], "http://tail-f.com/ns/ncs:t47": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t47"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t45": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t45"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t49": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t49"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t29": [{"range": {"value": [["1", "4294967"]]}, "name": "http://tail-f.com/ns/ncs:t29"}, {"name": "uint32"}], "http://tail-f.com/ns/ncs:t28": [{"range": {"value": [["1", "4294967"]]}, "name": "http://tail-f.com/ns/ncs:t28"}, {"name": "uint32"}], "http://tail-f.com/ns/ncs:node-name": [{"name": "http://tail-f.com/ns/ncs:node-name"}, {"max-length": {"value": 253}, "min-length": {"value": 1}, "name": "urn:ietf:params:xml:ns:yang:ietf-inet-types:domain-name", "pattern": {"value": "((([a-zA-Z0-9_]([a-zA-Z0-9\\-_]){0,61})?[a-zA-Z0-9]\\.)*([a-zA-Z0-9_]([a-zA-Z0-9\\-_]){0,61})?[a-zA-Z0-9]\\.?)|\\."}}, {"name": "string"}], "http://tail-f.com/ns/ncs:t74": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t74"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t72": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t72"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t70": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t70"}], "leaf_type": [{"name": "string"}]}], "urn:ietf:params:xml:ns:yang:ietf-inet-types:port-number": [{"range": {"value": [["0", "65535"]]}, "name": "urn:ietf:params:xml:ns:yang:ietf-inet-types:port-number"}, {"name": "uint16"}], "http://tail-f.com/ns/ncs:t56": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t56"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:t58": [{"list_type": [{"leaf-list": true, "name": "http://tail-f.com/ns/ncs:t58"}], "leaf_type": [{"name": "string"}]}]}, "keypath": "/ncs:devices"}, "data": {"info": {"string": "The managed devices and device communication settings"}, "kind": "container", "mandatory": true, "name": "devices", "contains_when_statement": true, "qname": "ncs:devices", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "children": [{"info": {"string": "Global settings for all managed devices."}, "kind": "container", "mandatory": true, "name": "global-settings", "qname": "ncs:global-settings", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Device profile parameters"}, "kind": "container", "mandatory": true, "name": "profiles", "qname": "ncs:profiles", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Authentication for managed devices"}, "kind": "container", "mandatory": true, "name": "authgroups", "qname": "ncs:authgroups", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Named configuration templates for devices"}, "kind": "list", "min_elements": 0, "name": "template", "max_elements": "unbounded", "qname": "ncs:template", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["name"], "mandatory": true, "children": [{"info": {"string": "The name of a specific template configuration."}, "kind": "key", "mandatory": true, "name": "name", "qname": "ncs:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "This container is augmented with data models from the devices."}, "kind": "container", "mandatory": true, "name": "config", "qname": "ncs:config", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}]}, {"info": {"string": "Groups of devices"}, "kind": "list", "leafref_groups": [["device-name"], ["device-group"], ["member"]], "min_elements": 0, "name": "device-group", "max_elements": "unbounded", "qname": "ncs:device-group", "children": [{"kind": "key", "mandatory": true, "name": "name", "qname": "ncs:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "Physical location of devices in the group"}, "kind": "container", "mandatory": true, "name": "location", "qname": "ncs:location", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Device within group"}, "kind": "leaf-list", "name": "device-name", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t56"}, "qname": "ncs:device-name", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:devices/device/name", "is_leafref": true}, {"info": {"string": "Group within group"}, "kind": "leaf-list", "name": "device-group", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t58"}, "qname": "ncs:device-group", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:devices/device-group/name", "is_leafref": true}, {"info": {"string": "Flattened list of all members"}, "kind": "leaf-list", "name": "member", "is_leafref": true, "qname": "ncs:member", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "leafref_target": "/ncs:devices/device/name", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t60"}, "config": false}, {"info": {"string": "RPCs from the device's"}, "kind": "container", "mandatory": true, "name": "rpc", "qname": "ncs:rpc", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "A summary of all active alarms per device group."}, "kind": "container", "mandatory": true, "name": "alarm-summary", "qname": "al:alarm-summary", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "Set up sessions to all unlocked devices"}, "kind": "action", "mandatory": true, "name": "connect", "qname": "ncs:connect", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Synchronize the config by pushing to the devices"}, "kind": "action", "mandatory": true, "name": "sync-to", "qname": "ncs:sync-to", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Synchronize the config by pulling from the devices"}, "kind": "action", "mandatory": true, "name": "sync-from", "qname": "ncs:sync-from", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Check if the NCS config is in sync with the device"}, "kind": "action", "mandatory": true, "name": "check-sync", "qname": "ncs:check-sync", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Check if NCS and the devices have compatible YANG modules"}, "kind": "action", "mandatory": true, "name": "check-yang-modules", "qname": "ncs:check-yang-modules", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Retrieve SSH host keys from all devices"}, "kind": "action", "mandatory": true, "name": "fetch-ssh-host-keys", "qname": "ncs:fetch-ssh-host-keys", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Take a named template and copy it here"}, "kind": "action", "mandatory": true, "name": "apply-template", "leafrefGroups": [["template-name"]], "qname": "ncs:apply-template", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["template-name"]]}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["name"], "mandatory": true, "leafrefGroups": [["device-name"], ["device-group"], ["member"]]}, {"info": {"string": "A list of named groups of MIBs"}, "kind": "list", "leafref_groups": [["mib-group"]], "min_elements": 0, "name": "mib-group", "max_elements": "unbounded", "qname": "ncs:mib-group", "children": [{"info": {"string": "An arbitrary name of the MIB group."}, "kind": "key", "mandatory": true, "name": "name", "qname": "ncs:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "MIB module names or name prefixes"}, "kind": "leaf-list", "name": "mib-module", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t68"}, "qname": "ncs:mib-module", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:devices/device-module/mib-module", "is_leafref": true}, {"info": {"string": "A list of MIB groups contained in this MIB group"}, "kind": "leaf-list", "name": "mib-group", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t70"}, "qname": "ncs:mib-group", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:devices/mib-group/name", "is_leafref": true}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["name"], "mandatory": true, "leafrefGroups": [["mib-group"]]}, {"info": {"string": "List the devices and supported modules"}, "kind": "list", "min_elements": 0, "name": "device-module", "max_elements": "unbounded", "qname": "ncs:device-module", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "key": ["name"], "mandatory": true, "config": false, "children": [{"info": {"string": "The module name"}, "kind": "key", "mandatory": true, "name": "name", "type": {"primitive": true, "name": "string"}, "qname": "ncs:name", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "The module revision"}, "kind": "leaf-list", "name": "revision", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t72"}, "qname": "ncs:revision", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "The XML namespace uri for the module"}, "kind": "leaf", "name": "uri", "type": {"primitive": true, "name": "string"}, "qname": "ncs:uri", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "The names of the devices that support this module"}, "kind": "leaf-list", "name": "devices", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t74"}, "qname": "ncs:devices", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}]}, {"info": {"string": "The list of managed devices"}, "kind": "list", "leafref_groups": [["remote-node"], ["authgroup"], ["device-profile"]], "min_elements": 0, "name": "device", "max_elements": "unbounded", "qname": "ncs:device", "children": [{"info": {"string": "A string uniquely identifying the managed device"}, "kind": "key", "mandatory": true, "name": "name", "qname": "ncs:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "IP address or host name for the management interface"}, "kind": "leaf", "name": "address", "qname": "ncs:address", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "host"}}, {"info": {"string": "Port for the management interface"}, "kind": "leaf", "name": "port", "qname": "ncs:port", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "port-number"}}, {"info": {"string": "Name of remote node which connects to device"}, "kind": "leaf", "name": "remote-node", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "node-name"}, "qname": "ncs:remote-node", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:cluster/remote-node/name", "is_leafref": true}, {"info": {"string": "SSH connection configuration"}, "kind": "container", "mandatory": true, "name": "ssh", "qname": "ncs:ssh", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Free form textual description"}, "kind": "leaf", "name": "description", "qname": "ncs:description", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "Physical location of devices in the group"}, "kind": "container", "mandatory": true, "name": "location", "qname": "ncs:location", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Authentication credentials for the device"}, "kind": "leaf", "name": "authgroup", "type": {"primitive": true, "name": "string"}, "qname": "ncs:authgroup", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:devices/authgroups/group/name", "is_leafref": true}, {"info": {"string": "Management protocol for the device"}, "kind": "container", "mandatory": true, "name": "device-type", "qname": "ncs:device-type", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"kind": "leaf", "name": "device-profile", "type": {"primitive": true, "name": "string"}, "qname": "ncs:device-profile", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:devices/profiles/profile/name", "is_leafref": true}, {"info": {"string": "Timeout in seconds for new connections"}, "kind": "leaf", "name": "connect-timeout", "qname": "ncs:connect-timeout", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "units": "seconds", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t27"}}, {"info": {"string": "Timeout in seconds used when reading data"}, "kind": "leaf", "name": "read-timeout", "qname": "ncs:read-timeout", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "units": "seconds", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t28"}}, {"info": {"string": "Timeout in seconds used when writing data"}, "kind": "leaf", "name": "write-timeout", "qname": "ncs:write-timeout", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "units": "seconds", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t29"}}, {"info": {"string": "Controls SSH keep alive settings"}, "kind": "container", "mandatory": true, "name": "ssh-keep-alive", "qname": "ncs:ssh-keep-alive", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Trace the southbound communication to devices"}, "kind": "leaf", "name": "trace", "qname": "ncs:trace", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "trace-flag"}}, {"info": {"string": "Control which device capabilities NCS uses"}, "kind": "container", "mandatory": true, "name": "ned-settings", "qname": "ncs:ned-settings", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Control settings for the commit-queue"}, "kind": "container", "mandatory": true, "name": "commit-queue", "qname": "ncs:commit-queue", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Control how sessions to related devices can be pooled."}, "kind": "container", "mandatory": true, "name": "session-pool", "qname": "ncs:session-pool", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Control settings for no-overwrite sync check"}, "kind": "container", "mandatory": true, "name": "no-overwrite", "qname": "ncs:no-overwrite", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Specifies the behaviour of a commit operation involving a\ndevice that is out of sync with NCS. Value accept assumes that\nthe device's sync state is unknown and it is cleared on commit.\nThe default behaviour is to reject such commits."}, "kind": "leaf", "name": "out-of-sync-commit-behaviour", "qname": "ncs:out-of-sync-commit-behaviour", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t40"}}, {"default": "use-lsa", "kind": "choice", "cases": [{"kind": "case", "name": "use-lsa", "children": [{"info": {"string": "Handle the LSA nodes as such. This is the default"}, "kind": "leaf", "name": "use-lsa", "qname": "ncs:use-lsa", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "no-lsa", "children": [{"info": {"string": "Do not handle any of the LSA nodes as such. These nodes\nwill be handled as any other device. This has the same\nresult as adding the commit flag 'no-lsa' to every commit."}, "kind": "leaf", "name": "no-lsa", "qname": "ncs:no-lsa", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"primitive": true, "name": "empty"}}]}], "name": "choice-lsa"}, {"info": {"string": "Show all active settings for the device"}, "kind": "container", "mandatory": true, "name": "active-settings", "qname": "ncs:active-settings", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "Additional protocols for the live-tree (read-only)"}, "kind": "list", "leafref_groups": [["authgroup"]], "min_elements": 0, "name": "live-status-protocol", "max_elements": "unbounded", "qname": "ncs:live-status-protocol", "children": [{"kind": "key", "mandatory": true, "name": "name", "qname": "ncs:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "IP Address for the management interface"}, "kind": "leaf", "name": "address", "qname": "ncs:address", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "host"}}, {"info": {"string": "Port for the management interface"}, "kind": "leaf", "name": "port", "qname": "ncs:port", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "port-number"}}, {"info": {"string": "SSH host key configuration"}, "kind": "container", "name": "ssh", "presence": true, "qname": "ncs:ssh", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}}, {"info": {"string": "Authentication credentials for the device"}, "kind": "leaf", "name": "authgroup", "type": {"primitive": true, "name": "string"}, "qname": "ncs:authgroup", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/ncs:devices/authgroups/group/name", "is_leafref": true}, {"info": {"string": "Management protocol for the device"}, "kind": "container", "mandatory": true, "name": "device-type", "qname": "ncs:device-type", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Operational State for the live protocol"}, "kind": "container", "mandatory": true, "name": "state", "qname": "ncs:state", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "List of capabillities supported by the device"}, "kind": "list", "min_elements": 0, "name": "capability", "max_elements": "unbounded", "qname": "ncs:capability", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "key": ["uri"], "mandatory": true, "config": false, "children": [{"info": {"string": "Capability URI"}, "kind": "key", "mandatory": true, "name": "uri", "type": {"primitive": true, "name": "string"}, "qname": "ncs:uri", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "Capability revision"}, "kind": "leaf", "name": "revision", "type": {"primitive": true, "name": "string"}, "qname": "ncs:revision", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Capability module"}, "kind": "leaf", "name": "module", "type": {"primitive": true, "name": "string"}, "qname": "ncs:module", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Capability features"}, "kind": "leaf-list", "name": "feature", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t83"}, "qname": "ncs:feature", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Capability deviations"}, "kind": "leaf-list", "name": "deviation", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t85"}, "qname": "ncs:deviation", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}]}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["name"], "mandatory": true, "leafrefGroups": [["authgroup"]]}, {"info": {"string": "Show states for the device"}, "kind": "container", "mandatory": true, "name": "state", "qname": "ncs:state", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "How the device was added to NCS"}, "kind": "container", "mandatory": true, "name": "source", "qname": "ncs:source", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "A list of capabilities supported by the device"}, "kind": "list", "min_elements": 0, "name": "capability", "max_elements": "unbounded", "qname": "ncs:capability", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "key": ["uri"], "mandatory": true, "config": false, "children": [{"kind": "key", "mandatory": true, "name": "uri", "type": {"primitive": true, "name": "string"}, "qname": "ncs:uri", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"kind": "leaf", "name": "revision", "config": false, "qname": "ncs:revision", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf", "name": "module", "config": false, "qname": "ncs:module", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf-list", "name": "feature", "config": false, "qname": "ncs:feature", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t43"}}, {"kind": "leaf-list", "name": "deviation", "config": false, "qname": "ncs:deviation", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t45"}}, {"info": {"string": "This action removes a capability from the list of capabilities.\nIf leaf module is set then corresponding module is attempted to\nbe removed from the list of modules for this device. This action\nis only intended to be used for pre-provisioning: it is not\npossible to override capabilities and modules provided by the\nNED implementation using this action."}, "kind": "action", "mandatory": true, "name": "remove", "qname": "ncs:remove", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}]}, {"info": {"string": "This is a list of the YANG modules supported by the device.\n\nThis list is populated the first time NCS connects to the\ndevice."}, "kind": "list", "min_elements": 0, "name": "module", "max_elements": "unbounded", "qname": "ncs:module", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "key": ["name"], "mandatory": true, "config": false, "children": [{"kind": "key", "mandatory": true, "name": "name", "type": {"primitive": true, "name": "string"}, "qname": "ncs:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"kind": "leaf", "name": "revision", "config": false, "qname": "ncs:revision", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf-list", "name": "feature", "config": false, "qname": "ncs:feature", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t47"}}, {"kind": "leaf-list", "name": "deviation", "config": false, "qname": "ncs:deviation", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t49"}}]}, {"info": {"string": "Contains vendor-specific information for\nidentifying the system platform.\n\nNEDs MAY augment this container with more device-specific\nnodes."}, "kind": "container", "mandatory": true, "name": "platform", "qname": "ncs:platform", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "NCS copy of the device configuration"}, "kind": "container", "mandatory": true, "name": "config", "qname": "ncs:config", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Status data fetched from the device"}, "kind": "container", "mandatory": true, "name": "live-status", "qname": "ncs:live-status", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "RPCs from the device"}, "kind": "container", "mandatory": true, "name": "rpc", "qname": "ncs:rpc", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "NETCONF notifications from the device"}, "kind": "container", "mandatory": true, "name": "netconf-notifications", "qname": "ncs:netconf-notifications", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}}, {"info": {"string": "Show services that use this device"}, "kind": "leaf-list", "name": "service-list", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "t101"}, "qname": "ncs:service-list", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Notification address if different from device address"}, "kind": "leaf", "name": "snmp-notification-address", "qname": "ncs:snmp-notification-address", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "host"}}, {"info": {"string": "Device specific information"}, "kind": "container", "name": "platform", "presence": true, "when_targets": ["/ncs:devices/device/device-type/cli/ned-id"], "qname": "alu-meta:platform", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}}, {"info": {"string": "A summary of all active alarms per device."}, "kind": "container", "mandatory": true, "name": "alarm-summary", "qname": "al:alarm-summary", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "Note: this action overwrites existing list of capabilities.\n\nThis action copies the list of capabilities and the list of modules\nfrom another device or profile. When used on a device, this action\nis only intended to be used for pre-provisioning: it is not possible\nto override capabilities and modules provided by the\nNED implementation using this action."}, "kind": "action", "mandatory": true, "name": "copy-capabilities", "qname": "ncs:copy-capabilities", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Note: this action overwrites existing list of capabilities.\n\nThis action populates the list of capabilities based on the\nconfigured ned-id for this device, if possible. NCS will look up\nthe package corresponding to the ned-id and add all the modules\nfrom this packages to the list of this device's capabilities and\nlist of modules. It is the responsibility of the caller to verify\nthat the automatically populated list of capabilities matches actual\ndevice's capabilities. The list of capabilities can then be\nfine-tuned using add-capability and capability/remove actions.\nCurrently this approach will only work for CLI and generic devices.\nThis action is only intended to be used for pre-provisioning:\nit is not possible to override capabilities and modules provided\nby the NED implementation using this action."}, "kind": "action", "mandatory": true, "name": "find-capabilities", "qname": "ncs:find-capabilities", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "This action adds a capability to the list of capabilities.\nIf uri is specified, then it is parsed as YANG capability string\nand module, revision, feature and deviation parameters are derived\nfrom the string. If module is specified, then the namespace is\nlooked up in the list of loaded namespaces and capability string\nconstructed automatically. If the module is specified and the\nattempt to look it up failed, then the action does nothing.\nIf module is specified or can be derived from capability string,\nthen the module is also added/replaced in the list of modules. This\naction is only intended to be used for pre-provisioning: it is not\npossible to override capabilities and modules provided by the NED\nimplementation using this action."}, "kind": "action", "mandatory": true, "name": "add-capability", "qname": "ncs:add-capability", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Take a named template and copy it here"}, "kind": "action", "mandatory": true, "name": "apply-template", "leafrefGroups": [["template-name"]], "qname": "ncs:apply-template", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["template-name"]]}, {"info": {"string": "Instantiate the config for the device from existing device"}, "kind": "action", "mandatory": true, "name": "instantiate-from-other-device", "leafrefGroups": [["device-name"]], "qname": "ncs:instantiate-from-other-device", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["device-name"]]}, {"info": {"string": "Compare the actual device config with the NCS copy"}, "kind": "action", "mandatory": true, "name": "compare-config", "qname": "ncs:compare-config", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Synchronize the config by pulling from the device"}, "kind": "action", "mandatory": true, "name": "sync-from", "qname": "ncs:sync-from", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Synchronize the config by pushing to the device"}, "kind": "action", "mandatory": true, "name": "sync-to", "qname": "ncs:sync-to", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Check if the NCS config is in sync with the device"}, "kind": "action", "mandatory": true, "name": "check-sync", "qname": "ncs:check-sync", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Check if NCS and the device have compatible YANG modules"}, "kind": "action", "mandatory": true, "name": "check-yang-modules", "qname": "ncs:check-yang-modules", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Connect to the device"}, "kind": "action", "mandatory": true, "name": "connect", "qname": "ncs:connect", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Close all sessions to the device"}, "kind": "action", "mandatory": true, "name": "disconnect", "qname": "ncs:disconnect", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "ICMP ping the device"}, "kind": "action", "mandatory": true, "name": "ping", "qname": "ncs:ping", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Delete the config in NCS without deleting it in the device"}, "kind": "action", "mandatory": true, "name": "delete-config", "qname": "ncs:delete-config", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Secure copy file to the device"}, "kind": "action", "mandatory": true, "name": "scp-to", "qname": "ncs:scp-to", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Secure copy file to the device"}, "kind": "action", "mandatory": true, "name": "scp-from", "qname": "ncs:scp-from", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["name"], "mandatory": true, "leafrefGroups": [["remote-node"], ["authgroup"], ["device-profile"]]}, {"info": {"string": "List of queued and completed commits"}, "kind": "container", "mandatory": true, "name": "commit-queue", "qname": "ncs:commit-queue", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "List of pooled NED sessions"}, "kind": "container", "mandatory": true, "name": "session-pool", "qname": "ncs:session-pool", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"info": {"string": "Set up sessions to all unlocked devices"}, "kind": "action", "mandatory": true, "name": "connect", "leafrefGroups": [["device"]], "qname": "ncs:connect", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["device"]]}, {"info": {"string": "Synchronize the config by pushing to the devices"}, "kind": "action", "mandatory": true, "name": "sync-to", "leafrefGroups": [["device"]], "qname": "ncs:sync-to", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["device"]]}, {"info": {"string": "Synchronize the config by pulling from the devices"}, "kind": "action", "mandatory": true, "name": "sync-from", "leafrefGroups": [["device"]], "qname": "ncs:sync-from", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["device"]]}, {"info": {"string": "Close all sessions to all devices"}, "kind": "action", "mandatory": true, "name": "disconnect", "qname": "ncs:disconnect", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Check if the NCS config is in sync with the device"}, "kind": "action", "mandatory": true, "name": "check-sync", "leafrefGroups": [["device"]], "qname": "ncs:check-sync", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["device"]]}, {"info": {"string": "Check if NCS and the devices have compatible YANG modules"}, "kind": "action", "mandatory": true, "name": "check-yang-modules", "leafrefGroups": [["device"]], "qname": "ncs:check-yang-modules", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["device"]]}, {"info": {"string": "Retrieve SSH host keys from all devices"}, "kind": "action", "mandatory": true, "name": "fetch-ssh-host-keys", "leafrefGroups": [["device"]], "qname": "ncs:fetch-ssh-host-keys", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "leafref_groups": [["device"]]}, {"info": {"string": "Clear all trace files"}, "kind": "action", "mandatory": true, "name": "clear-trace", "qname": "ncs:clear-trace", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Synchronize parts of the devices' configuration by pulling from\nthe network."}, "kind": "action", "mandatory": true, "name": "partial-sync-from", "qname": "ncs:partial-sync-from", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}]}} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/l3vpn_l3vpn_endpoint_schema.json b/tests/unit/plugins/modules/network/nso/fixtures/l3vpn_l3vpn_endpoint_schema.json deleted file mode 100644 index 0330aeb9..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/l3vpn_l3vpn_endpoint_schema.json +++ /dev/null @@ -1 +0,0 @@ -{"meta": {"prefix": "l3vpn", "namespace": "http://com/example/l3vpn", "types": {}, "keypath": "/l3vpn:vpn/l3vpn/endpoint"}, "data": {"kind": "list", "leafref_groups": [["ce-device"]], "min_elements": 0, "name": "endpoint", "max_elements": "unbounded", "qname": "l3vpn:endpoint", "children": [{"info": {"string": "Endpoint identifier"}, "kind": "key", "mandatory": true, "name": "id", "qname": "l3vpn:id", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf", "mandatory": true, "name": "ce-device", "type": {"primitive": true, "name": "string"}, "qname": "l3vpn:ce-device", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "leafref_target": "/ncs:devices/device/name", "is_leafref": true}, {"kind": "leaf", "mandatory": true, "name": "ce-interface", "qname": "l3vpn:ce-interface", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf", "mandatory": true, "name": "ip-network", "qname": "l3vpn:ip-network", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "ip-prefix"}}, {"info": {"string": "Bandwidth in bps"}, "kind": "leaf", "mandatory": true, "name": "bandwidth", "qname": "l3vpn:bandwidth", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "uint32"}}, {"info": {"string": "CE Router as-number"}, "kind": "leaf", "name": "as-number", "qname": "l3vpn:as-number", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"primitive": true, "name": "uint32"}}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["id"], "mandatory": true, "leafrefGroups": [["ce-device"]]}} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/l3vpn_l3vpn_schema.json b/tests/unit/plugins/modules/network/nso/fixtures/l3vpn_l3vpn_schema.json deleted file mode 100644 index 2737e7a5..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/l3vpn_l3vpn_schema.json +++ /dev/null @@ -1 +0,0 @@ -{"meta": {"prefix": "l3vpn", "namespace": "http://com/example/l3vpn", "types": {"http://com/example/l3vpn:t19": [{"list_type": [{"leaf-list": true, "name": "http://com/example/l3vpn:t19"}], "leaf_type": [{"name": "instance-identifier"}]}], "urn:ietf:params:xml:ns:yang:ietf-inet-types:port-number": [{"range": {"value": [["0", "65535"]]}, "name": "urn:ietf:params:xml:ns:yang:ietf-inet-types:port-number"}, {"name": "uint16"}], "http://tail-f.com/ns/ncs:outformat-deep-check-sync": [{"name": "http://tail-f.com/ns/ncs:outformat-deep-check-sync", "enumeration": [{"info": "The CLI config that would have to be applied\nto the device(s) in order for the service to\nbecome in sync with the network.", "label": "cli"}, {"info": "The XML (NETCONF format) that would have to be\napplied to the device(s) in order for the service to\nbecome in sync with the network.", "label": "xml"}, {"info": "Returns if the service is in sync or not.", "label": "boolean"}]}, {"name": "string"}], "http://com/example/l3vpn:t21": [{"list_type": [{"leaf-list": true, "name": "http://com/example/l3vpn:t21"}], "leaf_type": [{"name": "string"}]}], "http://com/example/l3vpn:t15": [{"list_type": [{"leaf-list": true, "name": "http://com/example/l3vpn:t15"}], "leaf_type": [{"name": "string"}]}], "http://com/example/l3vpn:protocol-type": [{"name": "http://com/example/l3vpn:protocol-type", "enumeration": [{"label": "icmp"}, {"label": "igmp"}, {"label": "ipip"}, {"label": "tcp"}, {"label": "egp"}, {"label": "udp"}, {"label": "rsvp"}, {"label": "gre"}, {"label": "esp"}, {"label": "ah"}, {"label": "icmp6"}, {"label": "ospf"}, {"label": "pim"}, {"label": "sctp"}]}, {"name": "string"}], "http://com/example/l3vpn:t17": [{"list_type": [{"leaf-list": true, "name": "http://com/example/l3vpn:t17"}], "leaf_type": [{"name": "instance-identifier"}]}], "http://com/example/l3vpn:t23": [{"list_type": [{"leaf-list": true, "name": "http://com/example/l3vpn:t23"}], "leaf_type": [{"name": "string"}]}], "http://com/example/l3vpn:t11": [{"list_type": [{"leaf-list": true, "name": "http://com/example/l3vpn:t11"}], "leaf_type": [{"name": "instance-identifier"}]}], "http://com/example/l3vpn:t13": [{"list_type": [{"leaf-list": true, "name": "http://com/example/l3vpn:t13"}], "leaf_type": [{"name": "instance-identifier"}]}], "http://com/example/l3vpn:t24": [{"name": "http://com/example/l3vpn:t24", "enumeration": [{"label": "waiting"}, {"label": "executing"}, {"label": "blocking"}, {"label": "blocked"}, {"label": "failed"}, {"label": "admin-cleared"}, {"label": "commit-queue-failed"}]}, {"name": "string"}], "http://tail-f.com/ns/ncs:log-entry-t": [{"info": "This leaf identifies the specific log entry.", "name": "http://tail-f.com/ns/ncs:log-entry-t", "enumeration": [{"label": "device-modified"}, {"label": "service-modified"}]}, {"name": "identityref"}], "http://com/example/l3vpn:t7": [{"name": "http://com/example/l3vpn:t7", "enumeration": [{"label": "async"}, {"label": "timeout"}, {"label": "deleted"}]}, {"name": "string"}], "http://com/example/l3vpn:qos-match-type": [{"union": [[{"name": "ipv4-address-and-prefix-length"}], [{"name": "http://com/example/l3vpn:t2", "enumeration": [{"label": "any"}]}, {"name": "string"}]]}], "http://com/example/l3vpn:t9": [{"list_type": [{"leaf-list": true, "name": "http://com/example/l3vpn:t9"}], "leaf_type": [{"name": "string"}]}], "http://tail-f.com/ns/ncs:outformat4": [{"name": "http://tail-f.com/ns/ncs:outformat4", "enumeration": [{"info": "NCS CLI curly bracket format.", "label": "cli"}, {"info": "NETCONF XML edit-config format, i.e., the edit-config that\nwould be applied locally (at NCS) to get a config\nthat is equal to that of the managed device.", "label": "xml"}, {"info": "The actual data in native format that would be sent to\nthe device", "label": "native"}, {"label": "boolean"}]}, {"name": "string"}], "http://tail-f.com/ns/ncs:log-entry-level-t": [{"info": "Levels used for identifying the severity of an event.\nLevels are organized from least specific to most where\n'all' is least specific and 'error' is most specific.", "name": "http://tail-f.com/ns/ncs:log-entry-level-t", "enumeration": [{"label": "all"}, {"label": "trace"}, {"label": "debug"}, {"label": "info"}, {"label": "warn"}, {"label": "error"}]}, {"name": "string"}], "http://tail-f.com/ns/ncs:outformat2": [{"name": "http://tail-f.com/ns/ncs:outformat2", "enumeration": [{"info": "NCS CLI curly bracket format.", "label": "cli"}, {"info": "NETCONF XML edit-config format, i.e., the edit-config that\nwould be applied locally (at NCS) to get a config\nthat is equal to that of the managed device.", "label": "xml"}]}, {"name": "string"}]}, "keypath": "/l3vpn:vpn/l3vpn"}, "data": {"kind": "list", "leafref_groups": [["used-by-customer-service"]], "min_elements": 0, "name": "l3vpn", "max_elements": "unbounded", "qname": "l3vpn:l3vpn", "children": [{"info": {"string": "Unique service id"}, "kind": "key", "mandatory": true, "name": "name", "qname": "l3vpn:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "Devices and other services this service modified directly or\nindirectly."}, "kind": "container", "mandatory": true, "name": "modified", "leafrefGroups": [["devices"]], "qname": "l3vpn:modified", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "leafref_groups": [["devices"]], "config": false, "children": [{"info": {"string": "Devices this service modified directly or indirectly"}, "kind": "leaf-list", "name": "devices", "is_leafref": true, "qname": "l3vpn:devices", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "leafref_target": "/ncs:devices/device/name", "type": {"namespace": "http://com/example/l3vpn", "name": "t9"}, "config": false}, {"info": {"string": "Services this service modified directly or indirectly"}, "kind": "leaf-list", "name": "services", "type": {"namespace": "http://com/example/l3vpn", "name": "t11"}, "qname": "l3vpn:services", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Services residing on remote LSA nodes this service\nhas modified directly or indirectly."}, "kind": "leaf-list", "name": "lsa-services", "type": {"namespace": "http://com/example/l3vpn", "name": "t13"}, "qname": "l3vpn:lsa-services", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}]}, {"info": {"string": "Devices and other services this service has explicitly\nmodified."}, "kind": "container", "mandatory": true, "name": "directly-modified", "leafrefGroups": [["devices"]], "qname": "l3vpn:directly-modified", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "leafref_groups": [["devices"]], "config": false, "children": [{"info": {"string": "Devices this service has explicitly modified."}, "kind": "leaf-list", "name": "devices", "is_leafref": true, "qname": "l3vpn:devices", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "leafref_target": "/ncs:devices/device/name", "type": {"namespace": "http://com/example/l3vpn", "name": "t15"}, "config": false}, {"info": {"string": "Services this service has explicitly modified."}, "kind": "leaf-list", "name": "services", "type": {"namespace": "http://com/example/l3vpn", "name": "t17"}, "qname": "l3vpn:services", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Services residing on remote LSA nodes this service\nhas explicitly modified."}, "kind": "leaf-list", "name": "lsa-services", "type": {"namespace": "http://com/example/l3vpn", "name": "t19"}, "qname": "l3vpn:lsa-services", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}]}, {"info": {"string": "A list of devices this service instance has manipulated"}, "kind": "leaf-list", "name": "device-list", "type": {"namespace": "http://com/example/l3vpn", "name": "t21"}, "qname": "l3vpn:device-list", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Customer facing services using this service"}, "kind": "leaf-list", "name": "used-by-customer-service", "is_leafref": true, "qname": "l3vpn:used-by-customer-service", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "leafref_target": "/ncs:services/customer-service/object-id", "type": {"namespace": "http://com/example/l3vpn", "name": "t23"}, "config": false}, {"kind": "container", "mandatory": true, "name": "commit-queue", "qname": "l3vpn:commit-queue", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false, "children": [{"kind": "list", "min_elements": 0, "name": "queue-item", "max_elements": "unbounded", "qname": "l3vpn:queue-item", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "key": ["id"], "mandatory": true, "config": false, "children": [{"kind": "key", "mandatory": true, "name": "id", "type": {"primitive": true, "name": "uint64"}, "qname": "l3vpn:id", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"kind": "leaf", "name": "status", "type": {"namespace": "http://com/example/l3vpn", "name": "t24"}, "qname": "l3vpn:status", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"kind": "list", "leafref_groups": [["name"]], "min_elements": 0, "name": "failed-device", "max_elements": "unbounded", "qname": "l3vpn:failed-device", "children": [{"kind": "key", "mandatory": true, "name": "name", "is_leafref": true, "qname": "l3vpn:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "leafref_target": "/ncs:devices/device/name", "type": {"primitive": true, "name": "string"}, "config": false}, {"kind": "leaf", "name": "time", "config": false, "qname": "l3vpn:time", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"primitive": true, "name": "date-and-time"}}, {"kind": "leaf", "name": "config-data", "is_cli_preformatted": true, "type": {"primitive": true, "name": "string"}, "qname": "l3vpn:config-data", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"kind": "leaf", "name": "error", "config": false, "qname": "l3vpn:error", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"primitive": true, "name": "string"}}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "key": ["name"], "mandatory": true, "config": false, "leafrefGroups": [["name"]]}, {"access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "kind": "action", "mandatory": true, "name": "admin-clear", "qname": "l3vpn:admin-clear"}, {"access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "kind": "action", "mandatory": true, "name": "delete", "qname": "l3vpn:delete"}]}, {"access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "kind": "action", "mandatory": true, "name": "clear", "qname": "l3vpn:clear"}]}, {"kind": "container", "mandatory": true, "name": "log", "qname": "l3vpn:log", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false, "children": [{"kind": "list", "min_elements": 0, "name": "log-entry", "max_elements": "unbounded", "qname": "l3vpn:log-entry", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "key": ["when"], "mandatory": true, "config": false, "children": [{"kind": "key", "mandatory": true, "name": "when", "type": {"primitive": true, "name": "date-and-time"}, "qname": "l3vpn:when", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"kind": "leaf", "mandatory": true, "name": "type", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "log-entry-t"}, "qname": "l3vpn:type", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"kind": "leaf", "mandatory": true, "name": "level", "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "log-entry-level-t"}, "qname": "l3vpn:level", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"kind": "leaf", "name": "message", "config": false, "qname": "l3vpn:message", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "type": {"primitive": true, "name": "string"}}]}, {"info": {"string": "Remove log entries"}, "kind": "action", "mandatory": true, "name": "purge", "qname": "l3vpn:purge", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}]}, {"kind": "leaf", "mandatory": true, "name": "route-distinguisher", "qname": "l3vpn:route-distinguisher", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "uint32"}}, {"kind": "list", "leafref_groups": [["ce-device"]], "min_elements": 0, "name": "endpoint", "max_elements": "unbounded", "qname": "l3vpn:endpoint", "children": [{"info": {"string": "Endpoint identifier"}, "kind": "key", "mandatory": true, "name": "id", "qname": "l3vpn:id", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf", "mandatory": true, "name": "ce-device", "type": {"primitive": true, "name": "string"}, "qname": "l3vpn:ce-device", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "leafref_target": "/ncs:devices/device/name", "is_leafref": true}, {"kind": "leaf", "mandatory": true, "name": "ce-interface", "qname": "l3vpn:ce-interface", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf", "mandatory": true, "name": "ip-network", "qname": "l3vpn:ip-network", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "ip-prefix"}}, {"info": {"string": "Bandwidth in bps"}, "kind": "leaf", "mandatory": true, "name": "bandwidth", "qname": "l3vpn:bandwidth", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "uint32"}}, {"info": {"string": "CE Router as-number"}, "kind": "leaf", "name": "as-number", "qname": "l3vpn:as-number", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"primitive": true, "name": "uint32"}}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["id"], "mandatory": true, "leafrefGroups": [["ce-device"]]}, {"kind": "container", "mandatory": true, "name": "qos", "leafrefGroups": [["qos-policy"]], "qname": "l3vpn:qos", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "leafref_groups": [["qos-policy"]], "children": [{"kind": "leaf", "name": "qos-policy", "type": {"primitive": true, "name": "string"}, "qname": "l3vpn:qos-policy", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "leafref_target": "/l3vpn:qos/qos-policy/name", "is_leafref": true}, {"kind": "list", "leafref_groups": [["qos-class"]], "min_elements": 0, "name": "custom-qos-match", "max_elements": "unbounded", "qname": "l3vpn:custom-qos-match", "children": [{"kind": "key", "mandatory": true, "name": "name", "qname": "l3vpn:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf", "mandatory": true, "name": "qos-class", "type": {"primitive": true, "name": "string"}, "qname": "l3vpn:qos-class", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "leafref_target": "/l3vpn:qos/qos-class/name", "is_leafref": true}, {"access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "kind": "leaf", "type": {"namespace": "http://com/example/l3vpn", "name": "qos-match-type"}, "name": "source-ip", "qname": "l3vpn:source-ip"}, {"access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "kind": "leaf", "type": {"namespace": "http://com/example/l3vpn", "name": "qos-match-type"}, "name": "destination-ip", "qname": "l3vpn:destination-ip"}, {"info": {"string": "Destination IP port"}, "kind": "leaf", "name": "port-start", "qname": "l3vpn:port-start", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "port-number"}}, {"info": {"string": "Destination IP port"}, "kind": "leaf", "name": "port-end", "qname": "l3vpn:port-end", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", "name": "port-number"}}, {"info": {"string": "Source IP protocol"}, "kind": "leaf", "name": "protocol", "qname": "l3vpn:protocol", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"namespace": "http://com/example/l3vpn", "name": "protocol-type"}}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["name"], "mandatory": true, "leafrefGroups": [["qos-class"]]}]}, {"info": {"string": "Check if device config is according to the service"}, "kind": "action", "mandatory": true, "name": "check-sync", "qname": "l3vpn:check-sync", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "children": [{"kind": "leaf", "is_action_input": true, "name": "outformat", "default": "boolean", "qname": "l3vpn:outformat", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "outformat4"}}, {"default": "deep", "kind": "choice", "cases": [{"kind": "case", "name": "deep", "children": [{"kind": "leaf", "is_action_input": true, "name": "deep", "qname": "l3vpn:deep", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "shallow", "children": [{"kind": "leaf", "is_action_input": true, "name": "shallow", "qname": "l3vpn:shallow", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}], "name": "depth"}, {"info": {"string": "Return list only contains negatives"}, "kind": "leaf", "is_action_input": true, "name": "suppress-positive-result", "qname": "l3vpn:suppress-positive-result", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}, {"kind": "choice", "cases": [{"kind": "case", "name": "use-lsa", "children": [{"kind": "leaf", "is_action_input": true, "name": "use-lsa", "qname": "l3vpn:use-lsa", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "no-lsa", "children": [{"kind": "leaf", "is_action_input": true, "name": "no-lsa", "qname": "l3vpn:no-lsa", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}], "name": "choice-lsa"}, {"kind": "choice", "cases": [{"kind": "case", "name": "in-sync", "children": [{"kind": "leaf", "name": "in-sync", "qname": "l3vpn:in-sync", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "type": {"primitive": true, "name": "boolean"}, "is_action_output": true}]}, {"kind": "case", "name": "case-xml", "children": [{"kind": "container", "mandatory": true, "name": "result-xml", "qname": "l3vpn:result-xml", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}, {"kind": "case", "name": "case-cli", "children": [{"kind": "container", "mandatory": true, "name": "cli", "qname": "l3vpn:cli", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}, {"kind": "case", "name": "case-native", "children": [{"kind": "container", "mandatory": true, "name": "native", "qname": "l3vpn:native", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}], "name": "outformat"}]}, {"info": {"string": "Check if device config is according to the service"}, "kind": "action", "mandatory": true, "name": "deep-check-sync", "qname": "l3vpn:deep-check-sync", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "children": [{"kind": "leaf", "is_action_input": true, "name": "outformat", "default": "boolean", "qname": "l3vpn:outformat", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "outformat-deep-check-sync"}}, {"info": {"string": "Return list only contains negatives"}, "kind": "leaf", "is_action_input": true, "name": "suppress-positive-result", "qname": "l3vpn:suppress-positive-result", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}, {"kind": "choice", "cases": [{"kind": "case", "name": "use-lsa", "children": [{"kind": "leaf", "is_action_input": true, "name": "use-lsa", "qname": "l3vpn:use-lsa", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "no-lsa", "children": [{"kind": "leaf", "is_action_input": true, "name": "no-lsa", "qname": "l3vpn:no-lsa", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}], "name": "choice-lsa"}, {"kind": "choice", "cases": [{"kind": "case", "name": "case-xml", "children": [{"kind": "container", "mandatory": true, "name": "result-xml", "qname": "l3vpn:result-xml", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}, {"kind": "case", "name": "case-cli", "children": [{"kind": "container", "mandatory": true, "name": "cli", "qname": "l3vpn:cli", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}, {"kind": "case", "name": "case-sync", "children": [{"kind": "container", "mandatory": true, "name": "sync-result", "qname": "l3vpn:sync-result", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}], "name": "outformat"}]}, {"info": {"string": "Run/Dryrun the service logic again"}, "kind": "action", "mandatory": true, "name": "re-deploy", "qname": "l3vpn:re-deploy", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "children": [{"kind": "container", "is_action_input": true, "name": "dry-run", "presence": true, "qname": "l3vpn:dry-run", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}}, {"kind": "leaf", "is_action_input": true, "name": "no-revision-drop", "qname": "l3vpn:no-revision-drop", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}, {"kind": "leaf", "is_action_input": true, "name": "no-networking", "qname": "l3vpn:no-networking", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}, {"kind": "choice", "cases": [{"kind": "case", "name": "no-overwrite", "children": [{"kind": "leaf", "is_action_input": true, "name": "no-overwrite", "qname": "l3vpn:no-overwrite", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "no-out-of-sync-check", "children": [{"kind": "leaf", "is_action_input": true, "name": "no-out-of-sync-check", "qname": "l3vpn:no-out-of-sync-check", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}], "name": "choice-sync-check"}, {"kind": "container", "is_action_input": true, "name": "commit-queue", "presence": true, "qname": "l3vpn:commit-queue", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}}, {"kind": "choice", "cases": [{"kind": "case", "name": "use-lsa", "children": [{"kind": "leaf", "is_action_input": true, "name": "use-lsa", "qname": "l3vpn:use-lsa", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "no-lsa", "children": [{"kind": "leaf", "is_action_input": true, "name": "no-lsa", "qname": "l3vpn:no-lsa", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}], "name": "choice-lsa"}, {"default": "deep", "kind": "choice", "cases": [{"kind": "case", "name": "deep", "children": [{"kind": "leaf", "is_action_input": true, "name": "deep", "qname": "l3vpn:deep", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "shallow", "children": [{"kind": "leaf", "is_action_input": true, "name": "shallow", "qname": "l3vpn:shallow", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}], "name": "depth"}, {"kind": "container", "is_action_input": true, "name": "reconcile", "presence": true, "qname": "l3vpn:reconcile", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}}, {"kind": "choice", "cases": [{"kind": "case", "name": "case-xml", "children": [{"kind": "container", "mandatory": true, "name": "result-xml", "qname": "l3vpn:result-xml", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}, {"kind": "case", "name": "case-cli", "children": [{"kind": "container", "mandatory": true, "name": "cli", "qname": "l3vpn:cli", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}, {"kind": "case", "name": "case-native", "children": [{"kind": "container", "mandatory": true, "name": "native", "qname": "l3vpn:native", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}], "name": "outformat"}, {"kind": "container", "mandatory": true, "name": "commit-queue", "qname": "l3vpn:commit-queue", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}, {"kind": "leaf", "name": "id", "type": {"primitive": true, "name": "uint64"}, "qname": "l3vpn:id", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "leafref_target": "/ncs:devices/commit-queue/queue-item/id", "is_leafref": true, "is_action_output": true}, {"kind": "leaf", "name": "tag", "qname": "l3vpn:tag", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "type": {"primitive": true, "name": "string"}, "is_action_output": true}, {"kind": "leaf", "name": "status", "qname": "l3vpn:status", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "type": {"namespace": "http://com/example/l3vpn", "name": "t7"}, "is_action_output": true}]}, {"info": {"string": "Reactive redeploy of service logic"}, "kind": "action", "mandatory": true, "name": "reactive-re-deploy", "qname": "l3vpn:reactive-re-deploy", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "children": [{"kind": "leaf", "name": "id", "type": {"primitive": true, "name": "uint64"}, "qname": "l3vpn:id", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "leafref_target": "/ncs:devices/commit-queue/queue-item/id", "is_leafref": true, "is_action_output": true}, {"kind": "leaf", "name": "tag", "qname": "l3vpn:tag", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "type": {"primitive": true, "name": "string"}, "is_action_output": true}, {"kind": "leaf", "name": "status", "qname": "l3vpn:status", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "type": {"namespace": "http://com/example/l3vpn", "name": "t7"}, "is_action_output": true}]}, {"info": {"string": "Touch a service"}, "kind": "action", "mandatory": true, "name": "touch", "qname": "l3vpn:touch", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "children": []}, {"info": {"string": "Get the data this service created"}, "kind": "action", "mandatory": true, "name": "get-modifications", "qname": "l3vpn:get-modifications", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "children": [{"kind": "leaf", "is_action_input": true, "name": "outformat", "qname": "l3vpn:outformat", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"namespace": "http://tail-f.com/ns/ncs", "name": "outformat2"}}, {"kind": "leaf", "is_action_input": true, "name": "reverse", "qname": "l3vpn:reverse", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}, {"default": "deep", "kind": "choice", "cases": [{"kind": "case", "name": "deep", "children": [{"kind": "leaf", "is_action_input": true, "name": "deep", "qname": "l3vpn:deep", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "shallow", "children": [{"kind": "leaf", "is_action_input": true, "name": "shallow", "qname": "l3vpn:shallow", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}], "name": "depth"}, {"kind": "choice", "cases": [{"kind": "case", "name": "use-lsa", "children": [{"kind": "leaf", "is_action_input": true, "name": "use-lsa", "qname": "l3vpn:use-lsa", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "no-lsa", "children": [{"kind": "leaf", "is_action_input": true, "name": "no-lsa", "qname": "l3vpn:no-lsa", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}], "name": "choice-lsa"}, {"kind": "choice", "cases": [{"kind": "case", "name": "case-xml", "children": [{"kind": "container", "mandatory": true, "name": "result-xml", "qname": "l3vpn:result-xml", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}, {"kind": "case", "name": "case-cli", "children": [{"kind": "container", "mandatory": true, "name": "cli", "qname": "l3vpn:cli", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}], "name": "outformat"}]}, {"info": {"string": "Undo the effects of this service"}, "kind": "action", "mandatory": true, "name": "un-deploy", "qname": "l3vpn:un-deploy", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}, "children": [{"kind": "container", "is_action_input": true, "name": "dry-run", "presence": true, "qname": "l3vpn:dry-run", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}}, {"kind": "leaf", "is_action_input": true, "name": "no-revision-drop", "qname": "l3vpn:no-revision-drop", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}, {"kind": "leaf", "is_action_input": true, "name": "no-networking", "qname": "l3vpn:no-networking", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}, {"kind": "choice", "cases": [{"kind": "case", "name": "no-overwrite", "children": [{"kind": "leaf", "is_action_input": true, "name": "no-overwrite", "qname": "l3vpn:no-overwrite", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "no-out-of-sync-check", "children": [{"kind": "leaf", "is_action_input": true, "name": "no-out-of-sync-check", "qname": "l3vpn:no-out-of-sync-check", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}], "name": "choice-sync-check"}, {"kind": "container", "is_action_input": true, "name": "commit-queue", "presence": true, "qname": "l3vpn:commit-queue", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}}, {"kind": "choice", "cases": [{"kind": "case", "name": "use-lsa", "children": [{"kind": "leaf", "is_action_input": true, "name": "use-lsa", "qname": "l3vpn:use-lsa", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}, {"kind": "case", "name": "no-lsa", "children": [{"kind": "leaf", "is_action_input": true, "name": "no-lsa", "qname": "l3vpn:no-lsa", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}]}], "name": "choice-lsa"}, {"kind": "leaf", "is_action_input": true, "name": "ignore-refcount", "qname": "l3vpn:ignore-refcount", "access": {"read": false, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "empty"}}, {"kind": "choice", "cases": [{"kind": "case", "name": "case-xml", "children": [{"kind": "container", "mandatory": true, "name": "result-xml", "qname": "l3vpn:result-xml", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}, {"kind": "case", "name": "case-cli", "children": [{"kind": "container", "mandatory": true, "name": "cli", "qname": "l3vpn:cli", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}, {"kind": "case", "name": "case-native", "children": [{"kind": "container", "mandatory": true, "name": "native", "qname": "l3vpn:native", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}]}], "name": "outformat"}, {"kind": "container", "mandatory": true, "name": "commit-queue", "qname": "l3vpn:commit-queue", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "is_action_output": true}, {"kind": "leaf", "name": "id", "type": {"primitive": true, "name": "uint64"}, "qname": "l3vpn:id", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "leafref_target": "/ncs:devices/commit-queue/queue-item/id", "is_leafref": true, "is_action_output": true}, {"kind": "leaf", "name": "tag", "qname": "l3vpn:tag", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "type": {"primitive": true, "name": "string"}, "is_action_output": true}, {"kind": "leaf", "name": "status", "qname": "l3vpn:status", "access": {"read": false, "create": false, "execute": false, "update": false, "delete": false}, "type": {"namespace": "http://com/example/l3vpn", "name": "t7"}, "is_action_output": true}]}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["name"], "mandatory": true, "leafrefGroups": [["used-by-customer-service"]]}} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/l3vpn_schema.json b/tests/unit/plugins/modules/network/nso/fixtures/l3vpn_schema.json deleted file mode 100644 index 0e7e3703..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/l3vpn_schema.json +++ /dev/null @@ -1 +0,0 @@ -{"meta": {"prefix": "l3vpn", "namespace": "http://com/example/l3vpn", "types": {"http://com/example/l3vpn:t21": [{"list_type": [{"leaf-list": true, "name": "http://com/example/l3vpn:t21"}], "leaf_type": [{"name": "string"}]}], "http://com/example/l3vpn:t23": [{"list_type": [{"leaf-list": true, "name": "http://com/example/l3vpn:t23"}], "leaf_type": [{"name": "string"}]}]}, "keypath": "/l3vpn:vpn"}, "data": {"kind": "container", "mandatory": true, "name": "vpn", "qname": "l3vpn:vpn", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "children": [{"kind": "list", "leafref_groups": [["used-by-customer-service"]], "min_elements": 0, "name": "l3vpn", "max_elements": "unbounded", "qname": "l3vpn:l3vpn", "children": [{"info": {"string": "Unique service id"}, "kind": "key", "mandatory": true, "name": "name", "qname": "l3vpn:name", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"info": {"string": "Devices and other services this service modified directly or\nindirectly."}, "kind": "container", "mandatory": true, "name": "modified", "leafrefGroups": [["devices"]], "qname": "l3vpn:modified", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "leafref_groups": [["devices"]], "config": false}, {"info": {"string": "Devices and other services this service has explicitly\nmodified."}, "kind": "container", "mandatory": true, "name": "directly-modified", "leafrefGroups": [["devices"]], "qname": "l3vpn:directly-modified", "is_config_false_callpoint": true, "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "leafref_groups": [["devices"]], "config": false}, {"info": {"string": "A list of devices this service instance has manipulated"}, "kind": "leaf-list", "name": "device-list", "type": {"namespace": "http://com/example/l3vpn", "name": "t21"}, "qname": "l3vpn:device-list", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "config": false}, {"info": {"string": "Customer facing services using this service"}, "kind": "leaf-list", "name": "used-by-customer-service", "is_leafref": true, "qname": "l3vpn:used-by-customer-service", "is_config_false_callpoint": true, "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "readonly": true, "leafref_target": "/ncs:services/customer-service/object-id", "type": {"namespace": "http://com/example/l3vpn", "name": "t23"}, "config": false}, {"kind": "container", "mandatory": true, "name": "commit-queue", "qname": "l3vpn:commit-queue", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"kind": "container", "mandatory": true, "name": "log", "qname": "l3vpn:log", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "readonly": true, "config": false}, {"kind": "leaf", "mandatory": true, "name": "route-distinguisher", "qname": "l3vpn:route-distinguisher", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "uint32"}}, {"kind": "list", "leafref_groups": [["ce-device"]], "min_elements": 0, "name": "endpoint", "max_elements": "unbounded", "qname": "l3vpn:endpoint", "children": [{"info": {"string": "Endpoint identifier"}, "kind": "key", "mandatory": true, "name": "id", "qname": "l3vpn:id", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf", "mandatory": true, "name": "ce-device", "type": {"primitive": true, "name": "string"}, "qname": "l3vpn:ce-device", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "leafref_target": "/ncs:devices/device/name", "is_leafref": true}, {"kind": "leaf", "mandatory": true, "name": "ce-interface", "qname": "l3vpn:ce-interface", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "string"}}, {"kind": "leaf", "mandatory": true, "name": "ip-network", "qname": "l3vpn:ip-network", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "ip-prefix"}}, {"info": {"string": "Bandwidth in bps"}, "kind": "leaf", "mandatory": true, "name": "bandwidth", "qname": "l3vpn:bandwidth", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "type": {"primitive": true, "name": "uint32"}}, {"info": {"string": "CE Router as-number"}, "kind": "leaf", "name": "as-number", "qname": "l3vpn:as-number", "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "type": {"primitive": true, "name": "uint32"}}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["id"], "mandatory": true, "leafrefGroups": [["ce-device"]]}, {"kind": "container", "mandatory": true, "name": "qos", "leafrefGroups": [["qos-policy"]], "qname": "l3vpn:qos", "access": {"read": true, "create": false, "execute": false, "update": true, "delete": false}, "leafref_groups": [["qos-policy"]]}, {"info": {"string": "Check if device config is according to the service"}, "kind": "action", "mandatory": true, "name": "check-sync", "qname": "l3vpn:check-sync", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Check if device config is according to the service"}, "kind": "action", "mandatory": true, "name": "deep-check-sync", "qname": "l3vpn:deep-check-sync", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Run/Dryrun the service logic again"}, "kind": "action", "mandatory": true, "name": "re-deploy", "qname": "l3vpn:re-deploy", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Reactive redeploy of service logic"}, "kind": "action", "mandatory": true, "name": "reactive-re-deploy", "qname": "l3vpn:reactive-re-deploy", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Touch a service"}, "kind": "action", "mandatory": true, "name": "touch", "qname": "l3vpn:touch", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Get the data this service created"}, "kind": "action", "mandatory": true, "name": "get-modifications", "qname": "l3vpn:get-modifications", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}, {"info": {"string": "Undo the effects of this service"}, "kind": "action", "mandatory": true, "name": "un-deploy", "qname": "l3vpn:un-deploy", "access": {"read": false, "create": false, "execute": true, "update": false, "delete": false}}], "access": {"read": true, "create": true, "execute": false, "update": true, "delete": true}, "key": ["name"], "mandatory": true, "leafrefGroups": [["used-by-customer-service"]]}]}} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/sync_from_schema.json b/tests/unit/plugins/modules/network/nso/fixtures/sync_from_schema.json deleted file mode 100644 index dc2206d4..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/sync_from_schema.json +++ /dev/null @@ -1,178 +0,0 @@ -{ - "meta": { - "prefix": "ncs", - "namespace": "http://tail-f.com/ns/ncs", - "types": { - "http://tail-f.com/ns/ncs:outformat2": [ - { - "name": "http://tail-f.com/ns/ncs:outformat2", - "enumeration": [ - { - "info": "NCS CLI curly bracket format.", - "label": "cli" - }, - { - "info": "NETCONF XML edit-config format, i.e., the edit-config that\nwould be applied locally (at NCS) to get a config\nthat is equal to that of the managed device.", - "label": "xml" - } - ] - }, - { - "name": "string" - } - ] - }, - "keypath": "/ncs:devices/device{ce0}/sync-from" - }, - "data": { - "info": { - "string": "Synchronize the config by pulling from the device" - }, - "kind": "action", - "mandatory": true, - "name": "sync-from", - "qname": "ncs:sync-from", - "access": { - "read": false, - "create": false, - "execute": true, - "update": false, - "delete": false - }, - "children": [ - { - "kind": "container", - "is_action_input": true, - "name": "dry-run", - "presence": true, - "qname": "ncs:dry-run", - "access": { - "read": false, - "create": false, - "execute": false, - "update": true, - "delete": false - }, - "children": [ - { - "info": { - "string": "Report what would be done towards CDB, without\nactually doing anything." - }, - "kind": "leaf", - "is_action_input": true, - "name": "outformat", - "qname": "ncs:outformat", - "access": { - "read": false, - "create": false, - "execute": false, - "update": true, - "delete": false - }, - "type": { - "namespace": "http://tail-f.com/ns/ncs", - "name": "outformat2" - } - } - ] - }, - { - "kind": "choice", - "cases": [ - { - "kind": "case", - "name": "result", - "children": [ - { - "kind": "leaf", - "name": "result", - "qname": "ncs:result", - "access": { - "read": false, - "create": false, - "execute": false, - "update": false, - "delete": false - }, - "type": { - "primitive": true, - "name": "boolean" - }, - "is_action_output": true - } - ] - }, - { - "kind": "case", - "name": "result-xml", - "children": [ - { - "kind": "leaf", - "name": "result-xml", - "is_cli_preformatted": true, - "qname": "ncs:result-xml", - "access": { - "read": false, - "create": false, - "execute": false, - "update": false, - "delete": false - }, - "type": { - "primitive": true, - "name": "string" - }, - "is_action_output": true - } - ] - }, - { - "kind": "case", - "name": "cli", - "children": [ - { - "kind": "leaf", - "name": "cli", - "is_cli_preformatted": true, - "qname": "ncs:cli", - "access": { - "read": false, - "create": false, - "execute": false, - "update": false, - "delete": false - }, - "type": { - "primitive": true, - "name": "string" - }, - "is_action_output": true - } - ] - } - ], - "name": "outformat" - }, - { - "info": { - "string": "If present, contains additional information about the result." - }, - "kind": "leaf", - "name": "info", - "qname": "ncs:info", - "access": { - "read": false, - "create": false, - "execute": false, - "update": false, - "delete": false - }, - "type": { - "primitive": true, - "name": "string" - }, - "is_action_output": true - } - ] - } -} diff --git a/tests/unit/plugins/modules/network/nso/fixtures/verify_violation_data.json b/tests/unit/plugins/modules/network/nso/fixtures/verify_violation_data.json deleted file mode 100644 index 05742c11..00000000 --- a/tests/unit/plugins/modules/network/nso/fixtures/verify_violation_data.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "tailf-ncs:devices": { - "device": [ - { - "name": "ce0", - "description": "Example Device" - } - ] - } -} diff --git a/tests/unit/plugins/modules/network/nso/nso_module.py b/tests/unit/plugins/modules/network/nso/nso_module.py deleted file mode 100644 index 23db9408..00000000 --- a/tests/unit/plugins/modules/network/nso/nso_module.py +++ /dev/null @@ -1,132 +0,0 @@ -# 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 - -import os -import json - -from ansible_collections.community.network.tests.unit.compat import unittest -from ansible_collections.community.network.tests.unit.compat.mock import patch -from ansible.module_utils import basic - - -fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') -fixture_data = {} - - -def load_fixture(name): - path = os.path.join(fixture_path, name) - if path not in fixture_data: - with open(path) as f: - data = json.load(f) - fixture_data[path] = data - return fixture_data[path] - - -class MockResponse(object): - def __init__(self, method, params, code, body, headers=None): - if headers is None: - headers = {} - - self.method = method - self.params = params - - self.code = code - self.body = body - self.headers = dict(headers) - - def read(self): - return self.body - - -def mock_call(calls, url, timeout, validate_certs, data=None, headers=None, method=None): - if len(calls) == 0: - raise ValueError('no call mock for method {0}({1})'.format( - url, data)) - - result = calls[0] - del calls[0] - - request = json.loads(data) - if result.method != request['method']: - raise ValueError('expected method {0}({1}), got {2}({3})'.format( - result.method, result.params, - request['method'], request['params'])) - - for key, value in result.params.items(): - if key not in request['params']: - raise ValueError('{0} not in parameters'.format(key)) - if value != request['params'][key]: - raise ValueError('expected {0} to be {1}, got {2}'.format( - key, value, request['params'][key])) - - return result - - -class AnsibleExitJson(Exception): - pass - - -class AnsibleFailJson(Exception): - pass - - -class TestNsoModule(unittest.TestCase): - - def execute_module(self, failed=False, changed=False, **kwargs): - if failed: - result = self.failed() - self.assertTrue(result['failed'], result) - else: - result = self.changed(changed) - self.assertEqual(result['changed'], changed, result) - - for key, value in kwargs.items(): - if key not in result: - self.fail("{0} not in result {1}".format(key, result)) - self.assertEqual(value, result[key]) - - return result - - def failed(self): - def fail_json(*args, **kwargs): - kwargs['failed'] = True - raise AnsibleFailJson(kwargs) - - with patch.object(basic.AnsibleModule, 'fail_json', fail_json): - with self.assertRaises(AnsibleFailJson) as exc: - self.module.main() - - result = exc.exception.args[0] - self.assertTrue(result['failed'], result) - return result - - def changed(self, changed=False): - def exit_json(*args, **kwargs): - if 'changed' not in kwargs: - kwargs['changed'] = False - raise AnsibleExitJson(kwargs) - - with patch.object(basic.AnsibleModule, 'exit_json', exit_json): - with self.assertRaises(AnsibleExitJson) as exc: - self.module.main() - - result = exc.exception.args[0] - self.assertEqual(result['changed'], changed, result) - return result diff --git a/tests/unit/plugins/modules/network/nso/test_nso_action.py b/tests/unit/plugins/modules/network/nso/test_nso_action.py deleted file mode 100644 index 82fe5842..00000000 --- a/tests/unit/plugins/modules/network/nso/test_nso_action.py +++ /dev/null @@ -1,167 +0,0 @@ -# -# 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 - -import json - -from ansible_collections.community.network.tests.unit.compat.mock import patch -from ansible_collections.community.network.plugins.modules.network.nso import nso_action -from . import nso_module -from .nso_module import MockResponse - -from ansible_collections.community.network.tests.unit.plugins.modules.utils import set_module_args - - -class TestNsoAction(nso_module.TestNsoModule): - module = nso_action - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_action_missing(self, open_url_mock): - action_input = {} - path = '/ncs:devices/device{ce0}/missing' - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5.0"}'), - MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'), - MockResponse('get_schema', {'path': path}, 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) - - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', - 'path': path, - 'input': action_input, - 'validate_certs': False - }) - self.execute_module(failed=True, msg='NSO get_schema invalid params. path = /ncs:devices/device{ce0}/missing') - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_action_not_action(self, open_url_mock): - action_input = {} - path = '/ncs:devices/device{ce0}/description' - schema = nso_module.load_fixture('description_schema.json') - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5.0"}'), - MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'), - MockResponse('get_schema', {'path': path}, 200, '{"result": %s}' % (json.dumps(schema, ))), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', - 'path': path, - 'input': action_input, - 'validate_certs': False - }) - self.execute_module(failed=True, msg='/ncs:devices/device{ce0}/description is not an action') - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_action_ok(self, open_url_mock): - action_input = {} - path = '/ncs:devices/device{ce0}/sync-from' - output = {"result": True} - schema = nso_module.load_fixture('sync_from_schema.json') - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5.0"}'), - MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'), - MockResponse('get_schema', {'path': path}, 200, '{"result": %s}' % (json.dumps(schema, ))), - MockResponse('run_action', {'path': path, 'params': action_input}, 200, '{"result": {"result": true}}'), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', - 'path': path, - 'input': action_input, - 'validate_certs': False - }) - self.execute_module(changed=True, output=output) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_action_validate_ok(self, open_url_mock): - action_input = {} - path = '/test:action' - output = {'version': [{'name': 'v1'}, {'name': 'v2'}]} - schema = nso_module.load_fixture('complex_schema.json') - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5.0"}'), - MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'), - MockResponse('get_schema', {'path': path}, 200, '{"result": %s}' % (json.dumps(schema, ))), - MockResponse('run_action', {'path': path, 'params': action_input}, 200, - '{"result": {"version": [{"name": "v1"}, {"name": "v2"}]}}'), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', - 'path': path, - 'input': action_input, - 'output_required': output, - 'validate_certs': False - }) - self.execute_module(changed=True, output=output) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_action_validate_failed(self, open_url_mock): - action_input = {} - path = '/test:action' - output_mismatch = {'version': [{'name': 'v1'}, {'name': 'v3'}]} - schema = nso_module.load_fixture('complex_schema.json') - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5.0"}'), - MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'), - MockResponse('get_schema', {'path': path}, 200, '{"result": %s}' % (json.dumps(schema, ))), - MockResponse('run_action', {'path': path, 'params': action_input}, 200, - '{"result": {"version": [{"name": "v1"}, {"name": "v2"}]}}'), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', - 'path': path, - 'input': action_input, - 'output_required': output_mismatch, - 'validate_certs': False - }) - self.execute_module(failed=True, msg="version value mismatch. expected [{'name': 'v1'}, {'name': 'v3'}] got [{'name': 'v1'}, {'name': 'v2'}]") - - self.assertEqual(0, len(calls)) diff --git a/tests/unit/plugins/modules/network/nso/test_nso_config.py b/tests/unit/plugins/modules/network/nso/test_nso_config.py deleted file mode 100644 index f86103ce..00000000 --- a/tests/unit/plugins/modules/network/nso/test_nso_config.py +++ /dev/null @@ -1,138 +0,0 @@ -# -# 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 - -import json - -from ansible_collections.community.network.tests.unit.compat.mock import patch -from ansible_collections.community.network.plugins.modules.network.nso import nso_config -from ansible_collections.community.network.tests.unit.plugins.modules.utils import set_module_args -from . import nso_module -from .nso_module import MockResponse - - -class TestNsoConfig(nso_module.TestNsoModule): - module = nso_config - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_config_invalid_version_short(self, open_url_mock): - self._test_invalid_version(open_url_mock, '3.3') - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_config_invalid_version_long(self, open_url_mock): - self._test_invalid_version(open_url_mock, '3.3.2') - - def _test_invalid_version(self, open_url_mock, version): - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "%s"}' % (version, )), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - data = nso_module.load_fixture('config_config.json') - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', 'data': data, - 'validate_certs': False - }) - self.execute_module(failed=True) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_config_valid_version_short(self, open_url_mock): - self._test_valid_version(open_url_mock, '4.5') - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_config_valid_version_long(self, open_url_mock): - self._test_valid_version(open_url_mock, '4.4.3') - - def _test_valid_version(self, open_url_mock, version): - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "%s"}' % (version, )), - MockResponse('new_trans', {}, 200, '{"result": {"th": 1}}'), - MockResponse('get_trans_changes', {}, 200, '{"result": {"changes": []}}'), - MockResponse('delete_trans', {}, 200, '{"result": {}}'), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - data = nso_module.load_fixture('config_empty_data.json') - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', 'data': data, - 'validate_certs': False - }) - self.execute_module(changed=False, changes=[], diffs=[]) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_config_changed(self, open_url_mock): - vpn_schema = nso_module.load_fixture('l3vpn_schema.json') - l3vpn_schema = nso_module.load_fixture('l3vpn_l3vpn_schema.json') - endpoint_schema = nso_module.load_fixture('l3vpn_l3vpn_endpoint_schema.json') - changes = nso_module.load_fixture('config_config_changes.json') - - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5.1"}'), - MockResponse('get_module_prefix_map', {}, 200, '{"result": {"l3vpn": "l3vpn"}}'), - MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'), - MockResponse('get_schema', {'path': '/l3vpn:vpn'}, 200, '{"result": %s}' % (json.dumps(vpn_schema, ))), - MockResponse('get_schema', {'path': '/l3vpn:vpn/l3vpn'}, 200, '{"result": %s}' % (json.dumps(l3vpn_schema, ))), - MockResponse('exists', {'path': '/l3vpn:vpn/l3vpn{company}'}, 200, '{"result": {"exists": true}}'), - MockResponse('get_schema', {'path': '/l3vpn:vpn/l3vpn/endpoint'}, 200, '{"result": %s}' % (json.dumps(endpoint_schema, ))), - MockResponse('exists', {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}'}, 200, '{"result": {"exists": false}}'), - MockResponse('new_trans', {'mode': 'read_write'}, 200, '{"result": {"th": 2}}'), - MockResponse('create', {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}'}, 200, '{"result": {}}'), - MockResponse('set_value', {'path': '/l3vpn:vpn/l3vpn{company}/route-distinguisher', 'value': 999}, 200, '{"result": {}}'), - MockResponse('set_value', {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/as-number', 'value': 65101}, 200, '{"result": {}}'), - MockResponse('set_value', {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/bandwidth', 'value': 12000000}, 200, '{"result": {}}'), - MockResponse('set_value', {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/ce-device', 'value': 'ce6'}, 200, '{"result": {}}'), - MockResponse('set_value', {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/ce-interface', - 'value': 'GigabitEthernet0/12'}, 200, '{"result": {}}'), - MockResponse('set_value', {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/ip-network', - 'value': '10.10.1.0/24'}, 200, '{"result": {}}'), - MockResponse('get_trans_changes', {}, 200, '{"result": %s}' % (json.dumps(changes), )), - MockResponse('validate_commit', {}, 200, '{"result": {}}'), - MockResponse('commit', {}, 200, '{"result": {}}'), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - data = nso_module.load_fixture('config_config.json') - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', 'data': data, - 'validate_certs': False - }) - self.execute_module(changed=True, changes=[ - {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/ce-device', 'type': 'set', 'from': None, 'to': 'ce6'}, - {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/ip-network', 'type': 'set', 'from': None, 'to': '10.10.1.0/24'}, - {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/as-number', 'type': 'set', 'from': None, 'to': '65101'}, - {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/ce-interface', 'type': 'set', 'from': None, 'to': 'GigabitEthernet0/12'}, - {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}/bandwidth', 'type': 'set', 'from': None, 'to': '12000000'}, - {'path': '/l3vpn:vpn/l3vpn{company}/endpoint{branch-office1}', 'type': 'create'}, - ], diffs=[]) - - self.assertEqual(0, len(calls)) diff --git a/tests/unit/plugins/modules/network/nso/test_nso_query.py b/tests/unit/plugins/modules/network/nso/test_nso_query.py deleted file mode 100644 index c1cd27de..00000000 --- a/tests/unit/plugins/modules/network/nso/test_nso_query.py +++ /dev/null @@ -1,57 +0,0 @@ -# -# 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 - -from ansible_collections.community.network.tests.unit.compat.mock import patch -from ansible_collections.community.network.plugins.modules.network.nso import nso_query -from . import nso_module -from .nso_module import MockResponse - -from ansible_collections.community.network.tests.unit.plugins.modules.utils import set_module_args - - -class TestNsoQuery(nso_module.TestNsoModule): - module = nso_query - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_query(self, open_url_mock): - xpath = '/packages/package' - fields = ['name', 'package-version'] - 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('query', - {'xpath_expr': xpath, 'selection': fields}, 200, - '{"result": {"results": [["test", "1.0"]]}}'), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', - 'xpath': xpath, - 'fields': fields, - 'validate_certs': False - }) - self.execute_module(changed=False, output=[["test", "1.0"]]) - - self.assertEqual(0, len(calls)) diff --git a/tests/unit/plugins/modules/network/nso/test_nso_show.py b/tests/unit/plugins/modules/network/nso/test_nso_show.py deleted file mode 100644 index 13dba834..00000000 --- a/tests/unit/plugins/modules/network/nso/test_nso_show.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# 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 - -from ansible_collections.community.network.tests.unit.compat.mock import patch -from ansible_collections.community.network.plugins.modules.network.nso import nso_show -from . import nso_module -from .nso_module import MockResponse - -from ansible_collections.community.network.tests.unit.plugins.modules.utils import set_module_args - - -class TestNsoShow(nso_module.TestNsoModule): - module = nso_show - - @patch('ansible_collections.community.network.plugins.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) - - 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_collections.community.network.plugins.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) - - 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_collections.community.network.plugins.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) - - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', - 'path': path, - 'operational': True, - 'validate_certs': False - }) - self.execute_module(changed=False, output={"data": {}}) - - self.assertEqual(0, len(calls)) diff --git a/tests/unit/plugins/modules/network/nso/test_nso_verify.py b/tests/unit/plugins/modules/network/nso/test_nso_verify.py deleted file mode 100644 index 7760382b..00000000 --- a/tests/unit/plugins/modules/network/nso/test_nso_verify.py +++ /dev/null @@ -1,110 +0,0 @@ -# -# 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 - -import json - -from ansible_collections.community.network.tests.unit.compat.mock import patch -from ansible_collections.community.network.plugins.modules.network.nso import nso_verify -from . import nso_module -from .nso_module import MockResponse - -from ansible_collections.community.network.tests.unit.plugins.modules.utils import set_module_args - - -class TestNsoVerify(nso_module.TestNsoModule): - module = nso_verify - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_verify_empty_data(self, open_url_mock): - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.4.3"}'), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - data = {} - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', 'data': data - }) - self.execute_module(changed=False) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_verify_violation(self, open_url_mock): - devices_schema = nso_module.load_fixture('devices_schema.json') - device_schema = nso_module.load_fixture('device_schema.json') - description_schema = nso_module.load_fixture('description_schema.json') - - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5.0"}'), - MockResponse('get_module_prefix_map', {}, 200, '{"result": {"tailf-ncs": "ncs"}}'), - MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'), - MockResponse('get_schema', {'path': '/ncs:devices'}, 200, '{"result": %s}' % (json.dumps(devices_schema, ))), - MockResponse('get_schema', {'path': '/ncs:devices/device'}, 200, '{"result": %s}' % (json.dumps(device_schema, ))), - MockResponse('exists', {'path': '/ncs:devices/device{ce0}'}, 200, '{"result": {"exists": true}}'), - MockResponse('get_value', {'path': '/ncs:devices/device{ce0}/description'}, 200, '{"result": {"value": "In Violation"}}'), - MockResponse('get_schema', {'path': '/ncs:devices/device/description'}, 200, '{"result": %s}' % (json.dumps(description_schema, ))), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - data = nso_module.load_fixture('verify_violation_data.json') - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', 'data': data - }) - self.execute_module(failed=True, violations=[ - {'path': '/ncs:devices/device{ce0}/description', 'expected-value': 'Example Device', 'value': 'In Violation'}, - ]) - - self.assertEqual(0, len(calls)) - - @patch('ansible_collections.community.network.plugins.module_utils.network.nso.nso.open_url') - def test_nso_verify_ok(self, open_url_mock): - devices_schema = nso_module.load_fixture('devices_schema.json') - device_schema = nso_module.load_fixture('device_schema.json') - - calls = [ - MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), - MockResponse('get_system_setting', {'operation': 'version'}, 200, '{"result": "4.5.0"}'), - MockResponse('get_module_prefix_map', {}, 200, '{"result": {"tailf-ncs": "ncs"}}'), - MockResponse('new_trans', {'mode': 'read'}, 200, '{"result": {"th": 1}}'), - MockResponse('get_schema', {'path': '/ncs:devices'}, 200, '{"result": %s}' % (json.dumps(devices_schema, ))), - MockResponse('get_schema', {'path': '/ncs:devices/device'}, 200, '{"result": %s}' % (json.dumps(device_schema, ))), - MockResponse('exists', {'path': '/ncs:devices/device{ce0}'}, 200, '{"result": {"exists": true}}'), - MockResponse('get_value', {'path': '/ncs:devices/device{ce0}/description'}, 200, '{"result": {"value": "Example Device"}}'), - MockResponse('logout', {}, 200, '{"result": {}}'), - ] - open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs) - - data = nso_module.load_fixture('verify_violation_data.json') - set_module_args({ - 'username': 'user', 'password': 'password', - 'url': 'http://localhost:8080/jsonrpc', 'data': data, - 'validate_certs': False - }) - self.execute_module(changed=False) - - self.assertEqual(0, len(calls)) From 50bd8ec1ab0dadb7733644202a5fdd83e4599ee5 Mon Sep 17 00:00:00 2001 From: Matt Mullen Date: Tue, 1 Dec 2020 09:30:46 -0500 Subject: [PATCH 2/4] kill nso zombies and add doc_fragment redirect --- plugins/doc_fragments/nso.py | 36 - plugins/module_utils/network/nso/__init__.py | 0 plugins/module_utils/network/nso/nso.py | 825 ------------------- plugins/modules/network/nso/nso_action.py | 184 ----- 4 files changed, 1045 deletions(-) delete mode 100644 plugins/doc_fragments/nso.py delete mode 100644 plugins/module_utils/network/nso/__init__.py delete mode 100644 plugins/module_utils/network/nso/nso.py delete mode 100644 plugins/modules/network/nso/nso_action.py diff --git a/plugins/doc_fragments/nso.py b/plugins/doc_fragments/nso.py deleted file mode 100644 index 74c16a2c..00000000 --- a/plugins/doc_fragments/nso.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Cisco and/or its affiliates. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class ModuleDocFragment(object): - - DOCUMENTATION = r''' -options: - url: - description: NSO JSON-RPC URL, http://localhost:8080/jsonrpc - type: str - required: true - username: - description: NSO username - type: str - required: true - password: - description: NSO password - type: str - required: true - timeout: - description: JSON-RPC request timeout in seconds - type: int - default: 300 - validate_certs: - description: When set to true, validates the SSL certificate of NSO when - using SSL - type: bool - required: false - default: false -''' diff --git a/plugins/module_utils/network/nso/__init__.py b/plugins/module_utils/network/nso/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/plugins/module_utils/network/nso/nso.py b/plugins/module_utils/network/nso/nso.py deleted file mode 100644 index 718926d9..00000000 --- a/plugins/module_utils/network/nso/nso.py +++ /dev/null @@ -1,825 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Cisco and/or its affiliates. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils.basic import env_fallback -from ansible.module_utils.urls import open_url -from ansible.module_utils._text import to_text - -import json -import re -import socket - -try: - unicode - HAVE_UNICODE = True -except NameError: - unicode = str - HAVE_UNICODE = False - - -nso_argument_spec = dict( - url=dict(type='str', required=True), - username=dict(type='str', required=True, fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])), - password=dict(type='str', required=True, no_log=True, fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD'])), - timeout=dict(type='int', default=300), - validate_certs=dict(type='bool', default=False) -) - - -class State(object): - SET = 'set' - PRESENT = 'present' - ABSENT = 'absent' - CHECK_SYNC = 'check-sync' - DEEP_CHECK_SYNC = 'deep-check-sync' - IN_SYNC = 'in-sync' - DEEP_IN_SYNC = 'deep-in-sync' - - SYNC_STATES = ('check-sync', 'deep-check-sync', 'in-sync', 'deep-in-sync') - - -class ModuleFailException(Exception): - def __init__(self, message): - super(ModuleFailException, self).__init__(message) - self.message = message - - -class NsoException(Exception): - def __init__(self, message, error): - super(NsoException, self).__init__(message) - self.message = message - self.error = error - - -class JsonRpc(object): - def __init__(self, url, timeout, validate_certs): - self._url = url - self._timeout = timeout - self._validate_certs = validate_certs - self._id = 0 - self._trans = {} - self._headers = {'Content-Type': 'application/json'} - self._conn = None - self._system_settings = {} - - def login(self, user, passwd): - payload = { - 'method': 'login', - 'params': {'user': user, 'passwd': passwd} - } - resp, resp_json = self._call(payload) - self._headers['Cookie'] = resp.headers['set-cookie'] - - def logout(self): - payload = {'method': 'logout', 'params': {}} - self._call(payload) - - def get_system_setting(self, setting): - if setting not in self._system_settings: - payload = {'method': 'get_system_setting', 'params': {'operation': setting}} - resp, resp_json = self._call(payload) - self._system_settings[setting] = resp_json['result'] - return self._system_settings[setting] - - def new_trans(self, **kwargs): - payload = {'method': 'new_trans', 'params': kwargs} - resp, resp_json = self._call(payload) - return resp_json['result']['th'] - - def get_trans(self, mode): - if mode not in self._trans: - th = self.new_trans(mode=mode) - self._trans[mode] = th - return self._trans[mode] - - def delete_trans(self, th): - payload = {'method': 'delete_trans', 'params': {'th': th}} - resp, resp_json = self._call(payload) - self._maybe_delete_trans(th) - - def validate_trans(self, th): - payload = {'method': 'validate_trans', 'params': {'th': th}} - resp, resp_json = self._write_call(payload) - return resp_json['result'] - - def get_trans_changes(self, th): - payload = {'method': 'get_trans_changes', 'params': {'th': th}} - resp, resp_json = self._write_call(payload) - return resp_json['result']['changes'] - - def validate_commit(self, th): - payload = {'method': 'validate_commit', 'params': {'th': th}} - resp, resp_json = self._write_call(payload) - return resp_json['result'].get('warnings', []) - - def commit(self, th): - payload = {'method': 'commit', 'params': {'th': th}} - resp, resp_json = self._write_call(payload) - if len(resp_json['result']) == 0: - self._maybe_delete_trans(th) - return resp_json['result'] - - def get_schema(self, **kwargs): - payload = {'method': 'get_schema', 'params': kwargs} - resp, resp_json = self._maybe_write_call(payload) - return resp_json['result'] - - def get_module_prefix_map(self, path=None): - if path is None: - payload = {'method': 'get_module_prefix_map', 'params': {}} - resp, resp_json = self._call(payload) - else: - payload = {'method': 'get_module_prefix_map', 'params': {'path': path}} - resp, resp_json = self._maybe_write_call(payload) - return resp_json['result'] - - def get_value(self, path): - payload = { - 'method': 'get_value', - 'params': {'path': path} - } - resp, resp_json = self._read_call(payload) - return resp_json['result'] - - def exists(self, path): - payload = {'method': 'exists', 'params': {'path': path}} - try: - resp, resp_json = self._read_call(payload) - return resp_json['result']['exists'] - except NsoException as ex: - # calling exists on a sub-list when the parent list does - # not exists will cause data.not_found errors on recent - # NSO - if 'type' in ex.error and ex.error['type'] == 'data.not_found': - return False - raise - - def create(self, th, path): - payload = {'method': 'create', 'params': {'th': th, 'path': path}} - self._write_call(payload) - - def delete(self, th, path): - payload = {'method': 'delete', 'params': {'th': th, 'path': path}} - self._write_call(payload) - - def set_value(self, th, path, value): - payload = { - 'method': 'set_value', - 'params': {'th': th, 'path': path, 'value': value} - } - 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 query(self, xpath, fields): - payload = { - 'method': 'query', - 'params': { - 'xpath_expr': xpath, - 'selection': fields - } - } - resp, resp_json = self._read_call(payload) - return resp_json['result']['results'] - - def run_action(self, th, path, params=None): - if params is None: - params = {} - - if is_version(self, [(4, 5), (4, 4, 3)]): - result_format = 'json' - else: - result_format = 'normal' - - payload = { - 'method': 'run_action', - 'params': { - 'format': result_format, - 'path': path, - 'params': params - } - } - if th is None: - resp, resp_json = self._read_call(payload) - else: - payload['params']['th'] = th - resp, resp_json = self._call(payload) - - if result_format == 'normal': - # this only works for one-level results, list entries, - # containers etc will have / in their name. - result = {} - for info in resp_json['result']: - result[info['name']] = info['value'] - else: - result = resp_json['result'] - - return result - - def _call(self, payload): - self._id += 1 - if 'id' not in payload: - payload['id'] = self._id - - if 'jsonrpc' not in payload: - payload['jsonrpc'] = '2.0' - - data = json.dumps(payload) - try: - resp = open_url( - self._url, timeout=self._timeout, - method='POST', data=data, headers=self._headers, - validate_certs=self._validate_certs) - if resp.code != 200: - raise NsoException( - 'NSO returned HTTP code {0}, expected 200'.format(resp.status), {}) - except socket.timeout: - raise NsoException('request timed out against NSO at {0}'.format(self._url), {}) - - resp_body = resp.read() - resp_json = json.loads(resp_body) - - if 'error' in resp_json: - self._handle_call_error(payload, resp_json) - return resp, resp_json - - def _handle_call_error(self, payload, resp_json): - method = payload['method'] - - error = resp_json['error'] - error_type = error['type'][len('rpc.method.'):] - if error_type in ('unexpected_params', - 'unknown_params_value', - 'invalid_params', - 'invalid_params_type', - 'data_not_found'): - key = error['data']['param'] - error_type_s = error_type.replace('_', ' ') - if key == 'path': - msg = 'NSO {0} {1}. path = {2}'.format( - method, error_type_s, payload['params']['path']) - else: - path = payload['params'].get('path', 'unknown') - msg = 'NSO {0} {1}. path = {2}. {3} = {4}'.format( - method, error_type_s, path, key, payload['params'][key]) - else: - msg = 'NSO {0} returned JSON-RPC error: {1}'.format(method, error) - - raise NsoException(msg, error) - - def _read_call(self, payload): - if 'th' not in payload['params']: - payload['params']['th'] = self.get_trans(mode='read') - return self._call(payload) - - def _write_call(self, payload): - if 'th' not in payload['params']: - payload['params']['th'] = self.get_trans(mode='read_write') - return self._call(payload) - - def _maybe_write_call(self, payload): - if 'read_write' in self._trans: - return self._write_call(payload) - else: - return self._read_call(payload) - - def _maybe_delete_trans(self, th): - for mode in ('read', 'read_write'): - if th == self._trans.get(mode, None): - del self._trans[mode] - - -class ValueBuilder(object): - PATH_RE = re.compile('{[^}]*}') - PATH_RE_50 = re.compile('{[^}]*}$') - - class Value(object): - __slots__ = ['path', 'tag_path', 'state', 'value', 'deps'] - - def __init__(self, path, state, value, deps): - self.path = path - self.tag_path = ValueBuilder.PATH_RE.sub('', path) - self.state = state - self.value = value - self.deps = deps - - # nodes can depend on themselves - if self.tag_path in self.deps: - self.deps.remove(self.tag_path) - - def __lt__(self, rhs): - l_len = len(self.path.split('/')) - r_len = len(rhs.path.split('/')) - if l_len == r_len: - return self.path.__lt__(rhs.path) - return l_len < r_len - - def __str__(self): - return 'Value'.format( - self.path, self.state, self.value) - - class ValueIterator(object): - def __init__(self, client, values, delayed_values): - self._client = client - self._values = values - self._delayed_values = delayed_values - self._pos = 0 - - def __iter__(self): - return self - - def __next__(self): - return self.next() - - def next(self): - if self._pos >= len(self._values): - if len(self._delayed_values) == 0: - raise StopIteration() - - builder = ValueBuilder(self._client, delay=False) - for (parent, maybe_qname, value) in self._delayed_values: - builder.build(parent, maybe_qname, value) - del self._delayed_values[:] - self._values.extend(builder.values) - - return self.next() - - value = self._values[self._pos] - self._pos += 1 - return value - - def __init__(self, client, mode='config', delay=None): - self._client = client - self._mode = mode - self._schema_cache = {} - self._module_prefix_map_cache = {} - self._values = [] - self._values_dirty = False - self._delay = delay is None and mode == 'config' and is_version(self._client, [(5, 0)]) - self._delayed_values = [] - - def build(self, parent, maybe_qname, value, schema=None): - qname, name = self.get_prefix_name(parent, maybe_qname) - if name is None: - path = parent - else: - path = '{0}/{1}'.format(parent, qname) - - if schema is None: - schema = self._get_schema(path) - - if self._delay and schema.get('is_mount_point', False): - # delay conversion of mounted values, required to get - # shema information on 5.0 and later. - self._delayed_values.append((parent, maybe_qname, value)) - elif self._is_leaf_list(schema) and is_version(self._client, [(4, 5)]): - self._build_leaf_list(path, schema, value) - elif self._is_leaf(schema): - deps = schema.get('deps', []) - if self._is_empty_leaf(schema): - exists = self._client.exists(path) - if exists and value != [None]: - self._add_value(path, State.ABSENT, None, deps) - elif not exists and value == [None]: - self._add_value(path, State.PRESENT, None, deps) - else: - if maybe_qname is None: - value_type = self.get_type(path) - else: - value_type = self._get_child_type(parent, qname) - - if 'identityref' in value_type: - if isinstance(value, list): - value = [ll_v for ll_v, t_ll_v - in [self.get_prefix_name(parent, v) for v in value]] - else: - value, t_value = self.get_prefix_name(parent, value) - self._add_value(path, State.SET, value, deps) - elif isinstance(value, dict): - self._build_dict(path, schema, value) - elif isinstance(value, list): - self._build_list(path, schema, value) - else: - raise ModuleFailException( - 'unsupported schema {0} at {1}'.format( - schema['kind'], path)) - - @property - def values(self): - if self._values_dirty: - self._values = ValueBuilder.sort_values(self._values) - self._values_dirty = False - - return ValueBuilder.ValueIterator(self._client, self._values, self._delayed_values) - - @staticmethod - def sort_values(values): - class N(object): - def __init__(self, v): - self.tmp_mark = False - self.mark = False - self.v = v - - sorted_values = [] - nodes = [N(v) for v in sorted(values)] - - def get_node(tag_path): - return next((m for m in nodes - if m.v.tag_path == tag_path), None) - - def is_cycle(n, dep, visited): - visited.add(n.v.tag_path) - if dep in visited: - return True - - dep_n = get_node(dep) - if dep_n is not None: - for sub_dep in dep_n.v.deps: - if is_cycle(dep_n, sub_dep, visited): - return True - - return False - - # check for dependency cycles, remove if detected. sort will - # not be 100% but allows for a best-effort to work around - # issue in NSO. - for n in nodes: - for dep in n.v.deps: - if is_cycle(n, dep, set()): - n.v.deps.remove(dep) - - def visit(n): - if n.tmp_mark: - return False - if not n.mark: - n.tmp_mark = True - for m in nodes: - if m.v.tag_path in n.v.deps: - if not visit(m): - return False - - n.tmp_mark = False - n.mark = True - - sorted_values.insert(0, n.v) - - return True - - n = next((n for n in nodes if not n.mark), None) - while n is not None: - visit(n) - n = next((n for n in nodes if not n.mark), None) - - return sorted_values[::-1] - - def _build_dict(self, path, schema, value): - keys = schema.get('key', []) - for dict_key, dict_value in value.items(): - qname, name = self.get_prefix_name(path, dict_key) - if dict_key in ('__state', ) or name in keys: - continue - - child_schema = self._find_child(path, schema, qname) - self.build(path, dict_key, dict_value, child_schema) - - def _build_leaf_list(self, path, schema, value): - deps = schema.get('deps', []) - entry_type = self.get_type(path, schema) - - if self._mode == 'verify': - for entry in value: - if 'identityref' in entry_type: - entry, t_entry = self.get_prefix_name(path, entry) - entry_path = '{0}{{{1}}}'.format(path, entry) - if not self._client.exists(entry_path): - self._add_value(entry_path, State.ABSENT, None, deps) - else: - # remove leaf list if treated as a list and then re-create the - # expected list entries. - self._add_value(path, State.ABSENT, None, deps) - - for entry in value: - if 'identityref' in entry_type: - entry, t_entry = self.get_prefix_name(path, entry) - entry_path = '{0}{{{1}}}'.format(path, entry) - self._add_value(entry_path, State.PRESENT, None, deps) - - def _build_list(self, path, schema, value): - deps = schema.get('deps', []) - for entry in value: - entry_key = self._build_key(path, entry, schema['key']) - entry_path = '{0}{{{1}}}'.format(path, entry_key) - entry_state = entry.get('__state', 'present') - entry_exists = self._client.exists(entry_path) - - if entry_state == 'absent': - if entry_exists: - self._add_value(entry_path, State.ABSENT, None, deps) - else: - if not entry_exists: - self._add_value(entry_path, State.PRESENT, None, deps) - if entry_state in State.SYNC_STATES: - self._add_value(entry_path, entry_state, None, deps) - - self.build(entry_path, None, entry) - - def _build_key(self, path, entry, schema_keys): - key_parts = [] - for key in schema_keys: - value = entry.get(key, None) - if value is None: - raise ModuleFailException( - 'required leaf {0} in {1} not set in data'.format( - key, path)) - - value_type = self._get_child_type(path, key) - if 'identityref' in value_type: - value, t_value = self.get_prefix_name(path, value) - key_parts.append(self._quote_key(value)) - return ' '.join(key_parts) - - def _quote_key(self, key): - if isinstance(key, bool): - return key and 'true' or 'false' - - q_key = [] - for c in str(key): - if c in ('{', '}', "'", '\\'): - q_key.append('\\') - q_key.append(c) - q_key = ''.join(q_key) - if ' ' in q_key: - return '"{0}"'.format(q_key) - return q_key - - def _find_child(self, path, schema, qname): - if 'children' not in schema: - schema = self._get_schema(path) - - # look for the qualified name if : is in the name - child_schema = self._get_child(schema, qname) - if child_schema is not None: - return child_schema - - # no child was found, look for a choice with a child matching - for child_schema in schema['children']: - if child_schema['kind'] != 'choice': - continue - choice_child_schema = self._get_choice_child(child_schema, qname) - if choice_child_schema is not None: - return choice_child_schema - - raise ModuleFailException( - 'no child in {0} with name {1}. children {2}'.format( - path, qname, ','.join((c.get('qname', c.get('name', None)) for c in schema['children'])))) - - def _add_value(self, path, state, value, deps): - self._values.append(ValueBuilder.Value(path, state, value, deps)) - self._values_dirty = True - - def get_prefix_name(self, path, qname): - if not isinstance(qname, (str, unicode)): - return qname, None - if ':' not in qname: - return qname, qname - - module_prefix_map = self._get_module_prefix_map(path) - module, name = qname.split(':', 1) - if module not in module_prefix_map: - raise ModuleFailException( - 'no module mapping for module {0}. loaded modules {1}'.format( - module, ','.join(sorted(module_prefix_map.keys())))) - - return '{0}:{1}'.format(module_prefix_map[module], name), name - - def _get_schema(self, path): - return self._ensure_schema_cached(path)['data'] - - def _get_child_type(self, parent_path, key): - all_schema = self._ensure_schema_cached(parent_path) - parent_schema = all_schema['data'] - meta = all_schema['meta'] - schema = self._find_child(parent_path, parent_schema, key) - return self.get_type(parent_path, schema, meta) - - def get_type(self, path, schema=None, meta=None): - if schema is None or meta is None: - all_schema = self._ensure_schema_cached(path) - schema = all_schema['data'] - meta = all_schema['meta'] - - if self._is_leaf(schema): - def get_type(meta, curr_type): - if curr_type.get('primitive', False): - return [curr_type['name']] - if 'namespace' in curr_type: - curr_type_key = '{0}:{1}'.format( - curr_type['namespace'], curr_type['name']) - type_info = meta['types'][curr_type_key][-1] - return get_type(meta, type_info) - if 'leaf_type' in curr_type: - return get_type(meta, curr_type['leaf_type'][-1]) - if 'union' in curr_type: - union_types = [] - for union_type in curr_type['union']: - union_types.extend(get_type(meta, union_type[-1])) - return union_types - return [curr_type.get('name', 'unknown')] - - return get_type(meta, schema['type']) - return None - - def _ensure_schema_cached(self, path): - if not self._delay and is_version(self._client, [(5, 0)]): - # newer versions of NSO support multiple different schemas - # for different devices, thus the device is required to - # look up the schema. Remove the key entry to get schema - # logic working ok. - path = ValueBuilder.PATH_RE_50.sub('', path) - else: - path = ValueBuilder.PATH_RE.sub('', path) - - if path not in self._schema_cache: - schema = self._client.get_schema(path=path, levels=1) - self._schema_cache[path] = schema - return self._schema_cache[path] - - def _get_module_prefix_map(self, path): - # newer versions of NSO support multiple mappings from module - # to prefix depending on which device is used. - if path != '' and is_version(self._client, [(5, 0)]): - if path not in self._module_prefix_map_cache: - self._module_prefix_map_cache[path] = self._client.get_module_prefix_map(path) - return self._module_prefix_map_cache[path] - - if '' not in self._module_prefix_map_cache: - self._module_prefix_map_cache[''] = self._client.get_module_prefix_map() - return self._module_prefix_map_cache[''] - - def _get_child(self, schema, qname): - # no child specified, return parent - if qname is None: - return schema - - name_key = ':' in qname and 'qname' or 'name' - return next((c for c in schema['children'] - if c.get(name_key, None) == qname), None) - - def _get_choice_child(self, schema, qname): - name_key = ':' in qname and 'qname' or 'name' - for child_case in schema['cases']: - # look for direct child - choice_child_schema = next( - (c for c in child_case['children'] - if c.get(name_key, None) == qname), None) - if choice_child_schema is not None: - return choice_child_schema - - # look for nested choice - for child_schema in child_case['children']: - if child_schema['kind'] != 'choice': - continue - choice_child_schema = self._get_choice_child(child_schema, qname) - if choice_child_schema is not None: - return choice_child_schema - return None - - def _is_leaf_list(self, schema): - return schema.get('kind', None) == 'leaf-list' - - def _is_leaf(self, schema): - # still checking for leaf-list here to be compatible with pre - # 4.5 versions of NSO. - return schema.get('kind', None) in ('key', 'leaf', 'leaf-list') - - def _is_empty_leaf(self, schema): - return (schema.get('kind', None) == 'leaf' and - schema['type'].get('primitive', False) and - schema['type'].get('name', '') == 'empty') - - -def connect(params): - client = JsonRpc(params['url'], - params['timeout'], - params['validate_certs']) - client.login(params['username'], params['password']) - return client - - -def verify_version(client, required_versions): - 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 is_version(client, required_versions): - version_str = client.get_system_setting('version') - return verify_version_str(version_str, required_versions) - - -def verify_version_str(version_str, required_versions): - version_str = re.sub('_.*', '', version_str) - - version = [int(p) for p in version_str.split('.')] - if len(version) < 2: - raise ModuleFailException( - 'unsupported NSO version format {0}'.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): - if value is None: - return None - if (isinstance(expected_value, bool) and - isinstance(value, (str, unicode))): - return value == 'true' - if isinstance(expected_value, int): - try: - return int(value) - except TypeError: - raise ModuleFailException( - 'returned value {0} for {1} is not a valid integer'.format( - key, value)) - if isinstance(expected_value, float): - try: - return float(value) - except TypeError: - raise ModuleFailException( - 'returned value {0} for {1} is not a valid float'.format( - key, value)) - if isinstance(expected_value, (list, tuple)): - if not isinstance(value, (list, tuple)): - raise ModuleFailException( - 'returned value {0} for {1} is not a list'.format(value, key)) - if len(expected_value) != len(value): - raise ModuleFailException( - 'list length mismatch for {0}'.format(key)) - - normalized_value = [] - for i in range(len(expected_value)): - normalized_value.append( - normalize_value(expected_value[i], value[i], '{0}[{1}]'.format(key, i))) - return normalized_value - - if isinstance(expected_value, dict): - if not isinstance(value, dict): - raise ModuleFailException( - 'returned value {0} for {1} is not a dict'.format(value, key)) - if len(expected_value) != len(value): - raise ModuleFailException( - 'dict length mismatch for {0}'.format(key)) - - normalized_value = {} - for k in expected_value.keys(): - n_k = normalize_value(k, k, '{0}[{1}]'.format(key, k)) - if n_k not in value: - raise ModuleFailException('missing {0} in value'.format(n_k)) - normalized_value[n_k] = normalize_value(expected_value[k], value[k], '{0}[{1}]'.format(key, k)) - return normalized_value - - if HAVE_UNICODE: - if isinstance(expected_value, unicode) and isinstance(value, str): - return value.decode('utf-8') - if isinstance(expected_value, str) and isinstance(value, unicode): - return value.encode('utf-8') - else: - if hasattr(expected_value, 'encode') and hasattr(value, 'decode'): - return value.decode('utf-8') - if hasattr(expected_value, 'decode') and hasattr(value, 'encode'): - return value.encode('utf-8') - - return value diff --git a/plugins/modules/network/nso/nso_action.py b/plugins/modules/network/nso/nso_action.py deleted file mode 100644 index 74ca48e0..00000000 --- a/plugins/modules/network/nso/nso_action.py +++ /dev/null @@ -1,184 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: nso_action -extends_documentation_fragment: -- community.network.nso - -short_description: Executes Cisco NSO actions and verifies output. -description: - - This module provides support for executing Cisco NSO actions and then - verifying that the output is as expected. -requirements: - - Cisco NSO version 3.4 or higher. -author: "Claes Nästén (@cnasten)" -options: - path: - description: Path to NSO action. - required: true - input: - description: > - NSO action parameters. - output_required: - description: > - Required output parameters. - output_invalid: - description: > - List of result parameter names that will cause the task to fail if they - are present. - validate_strict: - description: > - If set to true, the task will fail if any output parameters not in - output_required is present in the output. - type: bool - default: false -''' - -EXAMPLES = ''' -- name: Sync NSO device - community.network.nso_action: - url: http://localhost:8080/jsonrpc - username: username - password: password - path: /ncs:devices/device{ce0}/sync-from - input: {} -''' - -RETURN = ''' -output: - description: Action output - returned: success - type: dict - sample: - result: true -''' - -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import normalize_value -from ansible_collections.community.network.plugins.module_utils.network.nso.nso import ModuleFailException, NsoException -from ansible.module_utils.basic import AnsibleModule - - -class NsoAction(object): - REQUIRED_VERSIONS = [ - (3, 4) - ] - - def __init__(self, check_mode, client, - path, input, - output_required, output_invalid, validate_strict): - self._check_mode = check_mode - self._client = client - self._path = path - self._input = input - self._output_required = output_required - self._output_invalid = output_invalid - self._validate_strict = validate_strict - - def main(self): - schema = self._client.get_schema(path=self._path) - if schema['data']['kind'] != 'action': - raise ModuleFailException('{0} is not an action'.format(self._path)) - - input_schema = [c for c in schema['data']['children'] - if c.get('is_action_input', False)] - - for key, value in self._input.items(): - child = next((c for c in input_schema if c['name'] == key), None) - if child is None: - raise ModuleFailException('no parameter {0}'.format(key)) - - # implement type validation in the future - - if self._check_mode: - return {} - else: - return self._run_and_verify() - - def _run_and_verify(self): - output = self._client.run_action(None, self._path, self._input) - for key, value in self._output_required.items(): - if key not in output: - raise ModuleFailException('{0} not in result'.format(key)) - - n_value = normalize_value(value, output[key], key) - if value != n_value: - msg = '{0} value mismatch. expected {1} got {2}'.format( - key, value, n_value) - raise ModuleFailException(msg) - - for key in self._output_invalid.keys(): - if key in output: - raise ModuleFailException('{0} not allowed in result'.format(key)) - - if self._validate_strict: - for name in output.keys(): - if name not in self._output_required: - raise ModuleFailException('{0} not allowed in result'.format(name)) - - return output - - -def main(): - argument_spec = dict( - path=dict(required=True), - input=dict(required=False, type='dict', default={}), - output_required=dict(required=False, type='dict', default={}), - output_invalid=dict(required=False, type='dict', default={}), - validate_strict=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_action = NsoAction( - module.check_mode, client, - p['path'], - p['input'], - p['output_required'], - p['output_invalid'], - p['validate_strict']) - try: - verify_version(client, NsoAction.REQUIRED_VERSIONS) - - output = nso_action.main() - client.logout() - module.exit_json(changed=True, 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() From 82f959373d7f96dfce0dfb9d851464f5ac23c352 Mon Sep 17 00:00:00 2001 From: Matt Mullen Date: Tue, 1 Dec 2020 09:53:29 -0500 Subject: [PATCH 3/4] add redirect for doc_fragment to runtime.yml --- meta/runtime.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/meta/runtime.yml b/meta/runtime.yml index 1316c3f0..06613c97 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -168,3 +168,7 @@ plugin_routing: terminal: routeros: redirect: community.routeros.routeros +doc_fragments: + nso: + redirect: cisco.nso.nso + \ No newline at end of file From 7c82c994cae207b7ecd14e887a50cb5f61f24663 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 1 Dec 2020 22:22:24 +0100 Subject: [PATCH 4/4] Update meta/runtime.yml --- meta/runtime.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/meta/runtime.yml b/meta/runtime.yml index 06613c97..09b840bf 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -168,7 +168,7 @@ plugin_routing: terminal: routeros: redirect: community.routeros.routeros -doc_fragments: - nso: - redirect: cisco.nso.nso - \ No newline at end of file + doc_fragments: + nso: + redirect: cisco.nso.nso +