From a64b97da42b9e0a5d1c407d64dfb2ba2ac4fc777 Mon Sep 17 00:00:00 2001 From: Senthil Kumar Ganesan Date: Thu, 30 Aug 2018 09:39:47 -0700 Subject: [PATCH] Support for Openswitch (OPX) Control Plane Services (CPS) Module (#44745) * Generic module to support Openswitch (OPX) Contorl Plane Services (CPS) * Address ansible-test sanity issues --- .github/BOTMETA.yml | 2 + lib/ansible/modules/network/opx/__init__.py | 0 lib/ansible/modules/network/opx/opx_cps.py | 365 ++++++++++++++++++ test/units/modules/network/opx/__init__.py | 0 .../network/opx/fixtures/opx_get_config.cfg | 10 + .../opx/fixtures/opx_operation_create.cfg | 13 + .../opx/fixtures/opx_operation_delete.cfg | 8 + .../opx/fixtures/opx_operation_get.cfg | 16 + .../opx/fixtures/opx_operation_get_db.cfg | 25 ++ .../opx/fixtures/opx_operation_set.cfg | 11 + test/units/modules/network/opx/opx_module.py | 91 +++++ .../units/modules/network/opx/test_opx_cps.py | 183 +++++++++ 12 files changed, 724 insertions(+) create mode 100644 lib/ansible/modules/network/opx/__init__.py create mode 100644 lib/ansible/modules/network/opx/opx_cps.py create mode 100644 test/units/modules/network/opx/__init__.py create mode 100644 test/units/modules/network/opx/fixtures/opx_get_config.cfg create mode 100644 test/units/modules/network/opx/fixtures/opx_operation_create.cfg create mode 100644 test/units/modules/network/opx/fixtures/opx_operation_delete.cfg create mode 100644 test/units/modules/network/opx/fixtures/opx_operation_get.cfg create mode 100644 test/units/modules/network/opx/fixtures/opx_operation_get_db.cfg create mode 100644 test/units/modules/network/opx/fixtures/opx_operation_set.cfg create mode 100644 test/units/modules/network/opx/opx_module.py create mode 100644 test/units/modules/network/opx/test_opx_cps.py diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 4bbd75e2f2..5e546ad5eb 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -249,6 +249,7 @@ files: ignored: stygstra maintainers: $team_networking $modules/network/ordnance/: alexanderturner djh00t + $modules/network/opx/: $team_dell $modules/network/ovs/: ignored: stygstra maintainers: rcarrillocruz @@ -894,6 +895,7 @@ macros: team_cloudstack: resmo dpassante team_cumulus: isharacomix jrrivers team_cyberark_conjur: jvanderhoof ryanprior + team_dell: skg-net manjuv1 dhivyap abirami-n team_extreme: bigmstone LindsayHill team_ipa: Nosmoht Akasurde fxfitz team_jboss: jairojunior wbrefvem Wolfant diff --git a/lib/ansible/modules/network/opx/__init__.py b/lib/ansible/modules/network/opx/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/ansible/modules/network/opx/opx_cps.py b/lib/ansible/modules/network/opx/opx_cps.py new file mode 100644 index 0000000000..c5e9fbb97e --- /dev/null +++ b/lib/ansible/modules/network/opx/opx_cps.py @@ -0,0 +1,365 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# This file is part of Ansible by Red Hat +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = """ +--- +module: opx_cps +version_added: "2.7" +author: "Senthil Kumar Ganesan (@skg-net)" +short_description: CPS operations on networking device running Openswitch (OPX) +description: + - Executes the given operation on the YANG object, using CPS API in the + networking device running OpenSwitch (OPX). It uses the YANG models + provided in https://github.com/open-switch/opx-base-model. +options: + module_name: + description: + - Yang path to be configured. + attr_type: + description: + - Attribute Yang type. + attr_data: + description: + - Attribute Yang path and thier correspoding data. + operation: + description: + - Operation to be performed on the object. + default: create + choices: ['delete', 'create', 'set', 'action', 'get'] + db: + description: + - Queries/Writes the specified yang path from/to the db. + type: bool + default: 'no' + qualifier: + description: + - A qualifier provides the type of object data to retrieve or act on. + default: target + choices: ['target', 'observed', 'proposed', 'realtime', 'registration', 'running', 'startup'] + commit_event: + description: + - Attempts to force the auto-commit event to the specified yang object. + type: bool + default: 'no' +""" + +EXAMPLES = """ +- name: Create VLAN + opx_cps: + module_name: "dell-base-if-cmn/if/interfaces/interface" + attr_data: { + "base-if-vlan/if/interfaces/interface/id": 230, + "if/interfaces/interface/name": "br230", + "if/interfaces/interface/type": "ianaift:l2vlan" + } + operation: "create" +- name: Get VLAN + opx_cps: + module_name: "dell-base-if-cmn/if/interfaces/interface" + attr_data: { + "if/interfaces/interface/name": "br230", + } + operation: "get" +""" + +RETURN = """ +response: + description: Output from the CPS transaction. + returned: always + type: dict + sample: {'...':'...'} +changed: + description: Returns if the CPS transaction was performed. + returned: when a CPS traction is performed. + type: dict + sample: {'...':'...'} +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.six import iteritems + +try: + import cps + import cps_object + import cps_utils + HAS_CPS = True +except ImportError: + HAS_CPS = False + + +def get_config(module): + + qualifier = module.params['qualifier'] + module_name = module.params['module_name'] + attr_type = module.params["attr_type"] + attr_data = module.params["attr_data"] + db = module.params["db"] + commit_event = module.params["commit_event"] + config = dict() + + configobj = parse_cps_parameters(module_name, qualifier, + attr_type, attr_data, "get", + db, commit_event) + cpsconfig = cps_get(configobj) + + if cpsconfig['response']: + for key, val in iteritems(cpsconfig['response'][0]['data']): + if key == 'cps/key_data': + config.update(val) + else: + config[key] = val + + return config + + +def diff_dict(base_data, compare_with_data): + """ + Wrapper API for gives difference between 2 dicts + with base_data as the base reference + + Parameters + ---------- + base_data : dict + compare_with_data : dict + + Return + ------ + returns difference of 2 input + + Raises + ------ + """ + planned_set = set(base_data.keys()) + discovered_set = set(compare_with_data.keys()) + intersect_set = planned_set.intersection(discovered_set) + changed_dict = {} + added_set = planned_set - intersect_set + # Keys part of added are new and put into changed_dict + if added_set: + for key in added_set: + changed_dict[key] = base_data[key] + + for key in intersect_set: + value = base_data[key] + + if isinstance(value, list): + p_list = base_data[key] if key in base_data else [] + d_list = compare_with_data[key] if key in compare_with_data else [] + set_diff = set(p_list) - set(d_list) + if set_diff: + changed_dict[key] = list(set_diff) + elif isinstance(value, dict): + dict_diff = diff_dict(base_data[key], + compare_with_data[key]) + if dict_diff: + changed_dict[key] = dict_diff + else: + if compare_with_data[key] != base_data[key]: + changed_dict[key] = base_data[key] + return changed_dict + + +def convert_cps_raw_list(raw_list): + resp_list = [] + if raw_list: + for raw_elem in raw_list: + processed_element = convert_cps_raw_data(raw_elem) + if processed_element: + raw_key = raw_elem['key'] + individual_element = {} + individual_element['data'] = processed_element + individual_element['key'] = (cps.qual_from_key(raw_key) + "/" + + cps.name_from_key(raw_key, 1)) + resp_list.append(individual_element) + return resp_list + + +def convert_cps_raw_data(raw_elem): + d = {} + obj = cps_object.CPSObject(obj=raw_elem) + for attr in raw_elem['data']: + d[attr] = obj.get_attr_data(attr) + return d + + +def parse_cps_parameters(module_name, qualifier, attr_type, + attr_data, operation=None, db=None, + commit_event=None): + + obj = cps_object.CPSObject(module=module_name, qual=qualifier) + + if operation: + obj.set_property('oper', operation) + + if attr_type: + for key, val in iteritems(attr_type): + cps_utils.cps_attr_types_map.add_type(key, val) + + for key, val in iteritems(attr_data): + + embed_attrs = key.split(',') + embed_attrs_len = len(embed_attrs) + if embed_attrs_len >= 3: + obj.add_embed_attr(embed_attrs, val, embed_attrs_len - 2) + else: + if isinstance(val, str): + val_list = val.split(',') + # Treat as list if value contains ',' but is not + # enclosed within {} + if len(val_list) == 1 or val.startswith('{'): + obj.add_attr(key, val) + else: + obj.add_attr(key, val_list) + else: + obj.add_attr(key, val) + + if db: + cps.set_ownership_type(obj.get_key(), 'db') + obj.set_property('db', True) + else: + obj.set_property('db', False) + + if commit_event: + cps.set_auto_commit_event(obj.get_key(), True) + obj.set_property('commit-event', True) + return obj + + +def cps_get(obj): + + RESULT = dict() + key = obj.get() + l = [] + cps.get([key], l) + + resp_list = convert_cps_raw_list(l) + + RESULT["response"] = resp_list + RESULT["changed"] = False + return RESULT + + +def cps_transaction(obj): + + RESULT = dict() + ch = {'operation': obj.get_property('oper'), 'change': obj.get()} + if cps.transaction([ch]): + RESULT["response"] = convert_cps_raw_list([ch['change']]) + RESULT["changed"] = True + else: + error_msg = "Transaction error while " + obj.get_property('oper') + raise RuntimeError(error_msg) + return RESULT + + +def main(): + """ + main entry point for module execution + """ + argument_spec = dict( + qualifier=dict(required=False, + default="target", + type='str', + choices=['target', 'observed', 'proposed', 'realtime', + 'registration', 'running', 'startup']), + module_name=dict(required=True, type='str'), + attr_type=dict(required=False, type='dict'), + attr_data=dict(required=True, type='dict'), + operation=dict(required=False, + default="create", + type='str', + choices=['delete', 'create', 'set', 'action', 'get']), + db=dict(required=False, default=False, type='bool'), + commit_event=dict(required=False, default=False, type='bool') + ) + + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=False) + + if not HAS_CPS: + module.fail_json(msg='CPS library required for this module') + + qualifier = module.params['qualifier'] + module_name = module.params['module_name'] + attr_type = module.params["attr_type"] + attr_data = module.params["attr_data"] + operation = module.params['operation'] + db = module.params["db"] + commit_event = module.params["commit_event"] + RESULT = dict(changed=False, db=False, commit_event=False) + obj = parse_cps_parameters(module_name, qualifier, attr_type, + attr_data, operation, db, commit_event) + + if db: + RESULT['db'] = True + if commit_event: + RESULT['commit_event'] = True + + try: + if operation == 'get': + RESULT.update(cps_get(obj)) + else: + config = get_config(module) + diff = attr_data + + if config: + candidate = dict() + for key, val in iteritems(attr_data): + if key == 'cps/key_data': + candidate.update(val) + else: + candidate[key] = val + diff = diff_dict(candidate, config) + + if operation == "delete": + if config: + RESULT.update({"config": config, + "candidate": attr_data, + "diff": diff}) + RESULT.update(cps_transaction(obj)) + else: + if diff: + if 'cps/key_data' in attr_data: + diff.update(attr_data['cps/key_data']) + obj = parse_cps_parameters(module_name, qualifier, + attr_type, diff, operation, + db, commit_event) + RESULT.update({"config": config, + "candidate": attr_data, + "diff": diff}) + RESULT.update(cps_transaction(obj)) + + except Exception as e: + module.fail_json(msg=str(type(e).__name__) + ": " + str(e)) + + module.exit_json(**RESULT) + + +if __name__ == '__main__': + main() diff --git a/test/units/modules/network/opx/__init__.py b/test/units/modules/network/opx/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/units/modules/network/opx/fixtures/opx_get_config.cfg b/test/units/modules/network/opx/fixtures/opx_get_config.cfg new file mode 100644 index 0000000000..37db58272e --- /dev/null +++ b/test/units/modules/network/opx/fixtures/opx_get_config.cfg @@ -0,0 +1,10 @@ + { + "base-if-vlan/if/interfaces/interface/id": 105, + "if/interfaces/interface/name": "br105", + "dell-base-if-cmn/if/interfaces/interface/if-index": 74, + "dell-if/if/interfaces/interface/learning-mode": 1, + "dell-if/if/interfaces/interface/mtu": 1532, + "dell-if/if/interfaces/interface/phys-address": "", + "dell-if/if/interfaces/interface/vlan-type": 1, + "if/interfaces/interface/enabled": 0 + } diff --git a/test/units/modules/network/opx/fixtures/opx_operation_create.cfg b/test/units/modules/network/opx/fixtures/opx_operation_create.cfg new file mode 100644 index 0000000000..cdecae6c19 --- /dev/null +++ b/test/units/modules/network/opx/fixtures/opx_operation_create.cfg @@ -0,0 +1,13 @@ + { + "data": { + "base-if-vlan/if/interfaces/interface/id": 105, + "cps/key_data": { + "if/interfaces/interface/name": "br105" + }, + "cps/object-group/return-code": 0, + "dell-base-if-cmn/if/interfaces/interface/if-index": 70, + "if/interfaces/interface/type": "ianaift:l2vlan" + }, + "key": "target/dell-base-if-cmn/if/interfaces/interface" + } + diff --git a/test/units/modules/network/opx/fixtures/opx_operation_delete.cfg b/test/units/modules/network/opx/fixtures/opx_operation_delete.cfg new file mode 100644 index 0000000000..ba0ab2f97e --- /dev/null +++ b/test/units/modules/network/opx/fixtures/opx_operation_delete.cfg @@ -0,0 +1,8 @@ + { + "data": { + "cps/object-group/return-code": 0, + "if/interfaces/interface/name": "br105" + }, + "key": "target/dell-base-if-cmn/if/interfaces/interface" + } + diff --git a/test/units/modules/network/opx/fixtures/opx_operation_get.cfg b/test/units/modules/network/opx/fixtures/opx_operation_get.cfg new file mode 100644 index 0000000000..08593db156 --- /dev/null +++ b/test/units/modules/network/opx/fixtures/opx_operation_get.cfg @@ -0,0 +1,16 @@ + { + "data": { + "base-if-vlan/if/interfaces/interface/id": 105, + "cps/key_data": { + "if/interfaces/interface/name": "br105" + }, + "dell-base-if-cmn/if/interfaces/interface/if-index": 74, + "dell-if/if/interfaces/interface/learning-mode": 1, + "dell-if/if/interfaces/interface/mtu": 1532, + "dell-if/if/interfaces/interface/phys-address": "", + "dell-if/if/interfaces/interface/vlan-type": 1, + "if/interfaces/interface/enabled": 0 + }, + "key": "target/dell-base-if-cmn/if/interfaces/interface" + } + diff --git a/test/units/modules/network/opx/fixtures/opx_operation_get_db.cfg b/test/units/modules/network/opx/fixtures/opx_operation_get_db.cfg new file mode 100644 index 0000000000..1f2571a077 --- /dev/null +++ b/test/units/modules/network/opx/fixtures/opx_operation_get_db.cfg @@ -0,0 +1,25 @@ + { + "data": { + "base-if-phy/if/interfaces/interface/learn-mode": 3, + "base-if-phy/if/interfaces/interface/npu-id": 0, + "base-if-phy/if/interfaces/interface/phy-media": 43, + "base-if-phy/if/interfaces/interface/port-id": 25, + "base-if-phy/if/interfaces/interface/tagging-mode": 3, + "dell-base-if-cmn/if/interfaces/interface/if-index": 10, + "dell-if/if/interfaces-state/interface/supported-speed": [ + 3, + 4, + 6 + ], + "dell-if/if/interfaces/interface/auto-negotiation": 1, + "dell-if/if/interfaces/interface/duplex": 1, + "dell-if/if/interfaces/interface/mode": 1, + "dell-if/if/interfaces/interface/mtu": 1532, + "dell-if/if/interfaces/interface/phys-address": "ec:f4:bb:fc:61:9a", + "dell-if/if/interfaces/interface/speed": 0, + "if/interfaces/interface/enabled": 0, + "if/interfaces/interface/name": "e101-001-0", + "if/interfaces/interface/type": "ianaift:ethernetCsmacd" + }, + "key": "target/dell-base-if-cmn/if/interfaces/interface" + diff --git a/test/units/modules/network/opx/fixtures/opx_operation_set.cfg b/test/units/modules/network/opx/fixtures/opx_operation_set.cfg new file mode 100644 index 0000000000..99c0354cf7 --- /dev/null +++ b/test/units/modules/network/opx/fixtures/opx_operation_set.cfg @@ -0,0 +1,11 @@ + { + "data": { + "cps/object-group/return-code": 0, + "dell-if/if/interfaces/interface/untagged-ports": [ + "e101-001-0" + ], + "if/interfaces/interface/name": "br105" + }, + "key": "target/dell-base-if-cmn/if/interfaces/interface" + } + diff --git a/test/units/modules/network/opx/opx_module.py b/test/units/modules/network/opx/opx_module.py new file mode 100644 index 0000000000..7b5ed21cb0 --- /dev/null +++ b/test/units/modules/network/opx/opx_module.py @@ -0,0 +1,91 @@ +# (c) 2018 Red Hat Inc. +# +# (c) 2018 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# 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 . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json + +from units.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase + + +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 in fixture_data: + return fixture_data[path] + + with open(path) as f: + data = f.read() + + try: + data = json.loads(data) + except: + pass + + fixture_data[path] = data + return data + + +class TestOpxModule(ModuleTestCase): + + def execute_module(self, failed=False, changed=False, + response=None, msg=None, db=None, + commit_event=None): + + self.load_fixtures(response) + + if failed: + result = self.failed(msg) + self.assertTrue(result['failed'], result) + else: + result = self.changed(changed, db) + self.assertEqual(result['changed'], changed, result) + + return result + + def failed(self, msg): + with self.assertRaises(AnsibleFailJson) as exc: + self.module.main() + + result = exc.exception.args[0] + self.assertTrue(result['failed'], result) + self.assertEqual(result['msg'], msg, result) + return result + + def changed(self, changed=False, db=None): + with self.assertRaises(AnsibleExitJson) as exc: + self.module.main() + + result = exc.exception.args[0] + print("res" + str(result) + "dv=" + str(db) + "ch=" + str(changed)) + self.assertEqual(result['changed'], changed, result) + if db: + self.assertEqual(result['db'], db, result) + + return result + + def load_fixtures(self, response=None): + pass diff --git a/test/units/modules/network/opx/test_opx_cps.py b/test/units/modules/network/opx/test_opx_cps.py new file mode 100644 index 0000000000..3b3cacb110 --- /dev/null +++ b/test/units/modules/network/opx/test_opx_cps.py @@ -0,0 +1,183 @@ +# +# (c) 2018 Red Hat Inc. +# +# (c) 2018 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# 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 . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.compat.tests.mock import patch, Mock, MagicMock +import sys +sys.modules['cps'] = Mock(QUALIFIERS=[ + "target", + "observed", + "proposed", + "realtime", + "registration", + "running", + "startup" +], OPERATIONS=[ + "delete", + "create", + "set", + "action", + "get" +]) +sys.modules['cps_object'] = Mock() +sys.modules['cps_utils'] = Mock() + +from ansible.modules.network.opx import opx_cps + +from units.modules.utils import set_module_args +from .opx_module import TestOpxModule, load_fixture + + +class TestOpxCpsModule(TestOpxModule): + + module = opx_cps + + def setUp(self): + super(TestOpxCpsModule, self).setUp() + + self.mock_cps_get = patch('ansible.modules.network.opx.opx_cps.cps_get') + self.cps_get = self.mock_cps_get.start() + + self.mock_cps_transaction = patch('ansible.modules.network.opx.opx_cps.cps_transaction') + self.cps_transaction = self.mock_cps_transaction.start() + + self.mock_parse_cps_parameters = patch('ansible.modules.network.opx.opx_cps.parse_cps_parameters') + self.parse_cps_parameters = self.mock_parse_cps_parameters.start() + + self.mock_get_config = patch('ansible.modules.network.opx.opx_cps.get_config') + self.get_config = self.mock_get_config.start() + + def tearDown(self): + super(TestOpxCpsModule, self).tearDown() + self.mock_cps_get.stop() + self.mock_cps_transaction.stop() + self.mock_parse_cps_parameters.stop() + self.mock_get_config.stop() + + def test_opx_operation_create(self): + resp = load_fixture('opx_operation_create.cfg') + attr_data = {"base-if-vlan/if/interfaces/interface/id": 105, + "if/interfaces/interface/type": "ianaift:l2vlan"} + module_name = "dell-base-if-cmn/if/interfaces/interface" + set_module_args(dict(module_name=module_name, operation="create", attr_data=attr_data)) + self.get_config.return_value = dict() + self.cps_transaction.return_value = dict(changed=True, response=resp) + self.execute_module(changed=True, response=resp) + self.assertEqual(self.parse_cps_parameters.call_count, 2) + self.assertEqual(self.cps_transaction.call_count, 1) + + def test_opx_operation_set(self): + resp = load_fixture('opx_operation_set.cfg') + config_data = load_fixture('opx_get_config.cfg') + attr_data = {"dell-if/if/interfaces/interface/untagged-ports": "e101-001-0", + "if/interfaces/interface/name": "br105"} + module_name = "dell-base-if-cmn/if/interfaces/interface" + set_module_args(dict(module_name=module_name, operation="set", attr_data=attr_data)) + self.get_config.return_value = config_data + self.cps_transaction.return_value = dict(changed=True, response=resp) + self.execute_module(changed=True, response=resp) + self.assertEqual(self.parse_cps_parameters.call_count, 2) + self.assertEqual(self.cps_transaction.call_count, 1) + + def test_opx_operation_delete(self): + resp = load_fixture('opx_operation_delete.cfg') + config_data = load_fixture('opx_get_config.cfg') + attr_data = {"if/interfaces/interface/name": "br105"} + module_name = "dell-base-if-cmn/if/interfaces/interface" + set_module_args(dict(module_name=module_name, operation="delete", attr_data=attr_data)) + self.get_config.return_value = config_data + self.cps_transaction.return_value = dict(changed=True, response=resp) + self.execute_module(changed=True, response=resp) + self.assertEqual(self.parse_cps_parameters.call_count, 1) + self.assertEqual(self.cps_transaction.call_count, 1) + + def test_opx_operation_delete_fail(self): + resp = load_fixture('opx_operation_delete.cfg') + attr_data = {"if/interfaces/interface/name": "br105"} + module_name = "dell-base-if-cmn/if/interfaces/interface" + set_module_args(dict(module_name=module_name, operation="delete", attr_data=attr_data)) + self.get_config.return_value = dict() + self.execute_module(changed=False) + self.assertEqual(self.parse_cps_parameters.call_count, 1) + self.assertEqual(self.cps_transaction.call_count, 0) + + def test_opx_operation_get(self): + resp = load_fixture('opx_operation_get.cfg') + attr_data = {"if/interfaces/interface/type": "ianaift:l2vlan"} + module_name = "dell-base-if-cmn/if/interfaces/interface" + set_module_args(dict(module_name=module_name, operation="get", attr_data=attr_data)) + self.cps_get.return_value = dict(changed=True, response=resp) + self.cps_transaction.return_value = None + self.execute_module(changed=True, response=resp) + self.assertEqual(self.parse_cps_parameters.call_count, 1) + self.assertEqual(self.cps_get.call_count, 1) + self.cps_transaction.assert_not_called() + + def test_opx_operation_set_fail(self): + attr_data = {"dell-if/if/interfaces/interface/untagged-ports": "e101-001-0", + "if/interfaces/interface/name": "br105"} + exp_msg = "RuntimeError: Transaction error while set" + module_name = "dell-base-if-cmn/if/interfaces/interface" + set_module_args(dict(module_name=module_name, operation="set", attr_data=attr_data)) + self.get_config.return_value = dict() + self.cps_transaction.side_effect = RuntimeError("Transaction error while set") + self.execute_module(failed=True, msg=exp_msg) + self.assertEqual(self.parse_cps_parameters.call_count, 2) + self.assertEqual(self.cps_transaction.call_count, 1) + + def test_opx_operation_create_fail(self): + attr_data = {"if/interfaces/interface/type": "ianaift:l2vlan"} + config_data = load_fixture('opx_get_config.cfg') + exp_msg = "RuntimeError: Transaction error while create" + module_name = "dell-base-if-cmn/if/interfaces/interface" + set_module_args(dict(module_name=module_name, operation="create", attr_data=attr_data)) + self.get_config.return_value = config_data + self.cps_transaction.side_effect = RuntimeError("Transaction error while create") + self.execute_module(failed=True, msg=exp_msg) + self.assertEqual(self.parse_cps_parameters.call_count, 2) + self.assertEqual(self.cps_transaction.call_count, 1) + + def test_opx_operation_get_db(self): + resp = load_fixture('opx_operation_get_db.cfg') + attr_data = {"if/interfaces/interface/name": "e101-001-0"} + module_name = "dell-base-if-cmn/if/interfaces/interface" + set_module_args(dict(module_name=module_name, operation="get", attr_data=attr_data, db=True)) + self.cps_get.return_value = dict(changed=True, response=resp) + self.cps_transaction.return_value = None + self.execute_module(changed=True, response=resp, db=True) + self.assertEqual(self.parse_cps_parameters.call_count, 1) + self.assertEqual(self.cps_get.call_count, 1) + self.cps_transaction.assert_not_called() + + def test_opx_operation_set_commit_event(self): + resp = load_fixture('opx_operation_set.cfg') + config_data = load_fixture('opx_get_config.cfg') + attr_data = {"dell-if/if/interfaces/interface/untagged-ports": "e101-001-0", + "if/interfaces/interface/name": "br105"} + module_name = "dell-base-if-cmn/if/interfaces/interface" + set_module_args(dict(module_name=module_name, operation="set", attr_data=attr_data, commit_event=True)) + self.get_config.return_value = config_data + self.cps_transaction.return_value = dict(changed=True, response=resp) + self.execute_module(changed=True, response=resp, commit_event=True) + self.assertEqual(self.parse_cps_parameters.call_count, 2) + self.assertEqual(self.cps_transaction.call_count, 1)