From fb6ed8d76ca96bc25560ffca599b24423f9524d2 Mon Sep 17 00:00:00 2001 From: Felipe Garcia Bulsoni Date: Thu, 24 Aug 2017 12:57:13 -0300 Subject: [PATCH] EthernetNetworkModule for HPE OneView (#28336) * Adding module to manage ethernet network on HPE OneView * Adding unit tests to EthernetNetwork module * Added OneViewModuleException custom exceptions to module - Removed exception imports from hpOneView - Updated unit tests * Fixing mock import inside ethernet network module unit test * Fixing issues found in METADATA by CI * Updated paths to use solution name instead of vendor name * Fixed documentation, removed redundant if and improved readability * Updated _bulk_present to use and return `result`, same way as _present * Changed __ to _ in private methods following ansible style * Fixed some example inconsistencies and turned states doc into a list * Added adriane-cardozo to list of maintainers --- lib/ansible/module_utils/oneview.py | 71 +++- .../oneview/oneview_ethernet_network.py | 251 +++++++++++ .../oneview/oneview_module_loader.py | 8 +- .../oneview/test_oneview_ethernet_network.py | 400 ++++++++++++++++++ 4 files changed, 721 insertions(+), 9 deletions(-) create mode 100644 lib/ansible/modules/remote_management/oneview/oneview_ethernet_network.py create mode 100644 test/units/modules/remote_management/oneview/test_oneview_ethernet_network.py diff --git a/lib/ansible/module_utils/oneview.py b/lib/ansible/module_utils/oneview.py index 49769d5c24..3c220e4700 100644 --- a/lib/ansible/module_utils/oneview.py +++ b/lib/ansible/module_utils/oneview.py @@ -36,10 +36,6 @@ import traceback try: from hpOneView.oneview_client import OneViewClient - from hpOneView.exceptions import (HPOneViewException, - HPOneViewTaskError, - HPOneViewValueError, - HPOneViewResourceNotFound) HAS_HPE_ONEVIEW = True except ImportError: HAS_HPE_ONEVIEW = False @@ -132,6 +128,69 @@ def _standardize_value(value): return str(value) +class OneViewModuleException(Exception): + """ + OneView base Exception. + + Attributes: + msg (str): Exception message. + oneview_response (dict): OneView rest response. + """ + + def __init__(self, data): + self.msg = None + self.oneview_response = None + + if isinstance(data, six.string_types): + self.msg = data + else: + self.oneview_response = data + + if data and isinstance(data, dict): + self.msg = data.get('message') + + if self.oneview_response: + Exception.__init__(self, self.msg, self.oneview_response) + else: + Exception.__init__(self, self.msg) + + +class OneViewModuleTaskError(OneViewModuleException): + """ + OneView Task Error Exception. + + Attributes: + msg (str): Exception message. + error_code (str): A code which uniquely identifies the specific error. + """ + + def __init__(self, msg, error_code=None): + super(OneViewModuleTaskError, self).__init__(msg) + self.error_code = error_code + + +class OneViewModuleValueError(OneViewModuleException): + """ + OneView Value Error. + The exception is raised when the data contains an inappropriate value. + + Attributes: + msg (str): Exception message. + """ + pass + + +class OneViewModuleResourceNotFound(OneViewModuleException): + """ + OneView Resource Not Found Exception. + The exception is raised when an associated resource was not found. + + Attributes: + msg (str): Exception message. + """ + pass + + @six.add_metaclass(abc.ABCMeta) class OneViewModuleBase(object): MSG_CREATED = 'Resource created successfully.' @@ -221,7 +280,7 @@ class OneViewModuleBase(object): It calls the inheritor 'execute_module' function and sends the return to the Ansible. - It handles any HPOneViewException in order to signal a failure to Ansible, with a descriptive error message. + It handles any OneViewModuleException in order to signal a failure to Ansible, with a descriptive error message. """ try: @@ -236,7 +295,7 @@ class OneViewModuleBase(object): self.module.exit_json(**result) - except HPOneViewException as exception: + except OneViewModuleException as exception: error_msg = '; '.join(to_native(e) for e in exception.args) self.module.fail_json(msg=error_msg, exception=traceback.format_exc()) diff --git a/lib/ansible/modules/remote_management/oneview/oneview_ethernet_network.py b/lib/ansible/modules/remote_management/oneview/oneview_ethernet_network.py new file mode 100644 index 0000000000..c2279bda65 --- /dev/null +++ b/lib/ansible/modules/remote_management/oneview/oneview_ethernet_network.py @@ -0,0 +1,251 @@ +#!/usr/bin/python +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: oneview_ethernet_network +short_description: Manage OneView Ethernet Network resources +description: + - Provides an interface to manage Ethernet Network resources. Can create, update, or delete. +version_added: "2.4" +requirements: + - hpOneView >= 3.1.0 +author: + - Felipe Bulsoni (@fgbulsoni) + - Thiago Miotto (@tmiotto) + - Adriane Cardozo (@adriane-cardozo) +options: + state: + description: + - Indicates the desired state for the Ethernet Network resource. + - C(present) will ensure data properties are compliant with OneView. + - C(absent) will remove the resource from OneView, if it exists. + - C(default_bandwidth_reset) will reset the network connection template to the default. + default: present + choices: [present, absent, default_bandwidth_reset] + data: + description: + - List with Ethernet Network properties. + required: true +extends_documentation_fragment: + - oneview + - oneview.validateetag +''' + +EXAMPLES = ''' +- name: Ensure that the Ethernet Network is present using the default configuration + oneview_ethernet_network: + config: '/etc/oneview/oneview_config.json' + state: present + data: + name: 'Test Ethernet Network' + vlanId: '201' + delegate_to: localhost + +- name: Update the Ethernet Network changing bandwidth and purpose + oneview_ethernet_network: + config: '/etc/oneview/oneview_config.json' + state: present + data: + name: 'Test Ethernet Network' + purpose: Management + bandwidth: + maximumBandwidth: 3000 + typicalBandwidth: 2000 + delegate_to: localhost + +- name: Ensure that the Ethernet Network is present with name 'Renamed Ethernet Network' + oneview_ethernet_network: + config: '/etc/oneview/oneview_config.json' + state: present + data: + name: 'Test Ethernet Network' + newName: 'Renamed Ethernet Network' + delegate_to: localhost + +- name: Ensure that the Ethernet Network is absent + oneview_ethernet_network: + config: '/etc/oneview/oneview_config.json' + state: absent + data: + name: 'New Ethernet Network' + delegate_to: localhost + +- name: Create Ethernet networks in bulk + oneview_ethernet_network: + config: '/etc/oneview/oneview_config.json' + state: present + data: + vlanIdRange: '1-10,15,17' + purpose: General + namePrefix: TestNetwork + smartLink: false + privateNetwork: false + bandwidth: + maximumBandwidth: 10000 + typicalBandwidth: 2000 + delegate_to: localhost + +- name: Reset to the default network connection template + oneview_ethernet_network: + config: '/etc/oneview/oneview_config.json' + state: default_bandwidth_reset + data: + name: 'Test Ethernet Network' + delegate_to: localhost +''' + +RETURN = ''' +ethernet_network: + description: Has the facts about the Ethernet Networks. + returned: On state 'present'. Can be null. + type: dict + +ethernet_network_bulk: + description: Has the facts about the Ethernet Networks affected by the bulk insert. + returned: When 'vlanIdRange' attribute is in data argument. Can be null. + type: dict + +ethernet_network_connection_template: + description: Has the facts about the Ethernet Network Connection Template. + returned: On state 'default_bandwidth_reset'. Can be null. + type: dict +''' + +from ansible.module_utils.oneview import OneViewModuleBase, OneViewModuleResourceNotFound + + +class EthernetNetworkModule(OneViewModuleBase): + MSG_CREATED = 'Ethernet Network created successfully.' + MSG_UPDATED = 'Ethernet Network updated successfully.' + MSG_DELETED = 'Ethernet Network deleted successfully.' + MSG_ALREADY_PRESENT = 'Ethernet Network is already present.' + MSG_ALREADY_ABSENT = 'Ethernet Network is already absent.' + + MSG_BULK_CREATED = 'Ethernet Networks created successfully.' + MSG_MISSING_BULK_CREATED = 'Some missing Ethernet Networks were created successfully.' + MSG_BULK_ALREADY_EXIST = 'The specified Ethernet Networks already exist.' + MSG_CONNECTION_TEMPLATE_RESET = 'Ethernet Network connection template was reset to the default.' + MSG_ETHERNET_NETWORK_NOT_FOUND = 'Ethernet Network was not found.' + + RESOURCE_FACT_NAME = 'ethernet_network' + + def __init__(self): + + argument_spec = dict( + state=dict(type='str', default='present', choices=['absent', 'default_bandwidth_reset', 'present']), + data=dict(type='dict', required=True), + ) + + super(EthernetNetworkModule, self).__init__(additional_arg_spec=argument_spec, validate_etag_support=True) + + self.resource_client = self.oneview_client.ethernet_networks + + def execute_module(self): + + changed, msg, ansible_facts, resource = False, '', {}, None + + if self.data.get('name'): + resource = self.get_by_name(self.data['name']) + + if self.state == 'present': + if self.data.get('vlanIdRange'): + return self._bulk_present() + else: + return self._present(resource) + elif self.state == 'absent': + return self.resource_absent(resource) + elif self.state == 'default_bandwidth_reset': + changed, msg, ansible_facts = self._default_bandwidth_reset(resource) + return dict(changed=changed, msg=msg, ansible_facts=ansible_facts) + + def _present(self, resource): + + bandwidth = self.data.pop('bandwidth', None) + scope_uris = self.data.pop('scopeUris', None) + result = self.resource_present(resource, self.RESOURCE_FACT_NAME) + + if bandwidth: + if self._update_connection_template(result['ansible_facts']['ethernet_network'], bandwidth)[0]: + result['changed'] = True + result['msg'] = self.MSG_UPDATED + + if scope_uris is not None: + result = self.resource_scopes_set(result, 'ethernet_network', scope_uris) + + return result + + def _bulk_present(self): + vlan_id_range = self.data['vlanIdRange'] + result = dict(ansible_facts={}) + ethernet_networks = self.resource_client.get_range(self.data['namePrefix'], vlan_id_range) + + if not ethernet_networks: + self.resource_client.create_bulk(self.data) + result['changed'] = True + result['msg'] = self.MSG_BULK_CREATED + + else: + vlan_ids = self.resource_client.dissociate_values_or_ranges(vlan_id_range) + for net in ethernet_networks[:]: + vlan_ids.remove(net['vlanId']) + + if len(vlan_ids) == 0: + result['msg'] = self.MSG_BULK_ALREADY_EXIST + result['changed'] = False + else: + if len(vlan_ids) == 1: + self.data['vlanIdRange'] = '{0}-{1}'.format(vlan_ids[0], vlan_ids[0]) + else: + self.data['vlanIdRange'] = ','.join(map(str, vlan_ids)) + + self.resource_client.create_bulk(self.data) + result['changed'] = True + result['msg'] = self.MSG_MISSING_BULK_CREATED + result['ansible_facts']['ethernet_network_bulk'] = self.resource_client.get_range(self.data['namePrefix'], vlan_id_range) + + return result + + def _update_connection_template(self, ethernet_network, bandwidth): + + if 'connectionTemplateUri' not in ethernet_network: + return False, None + + connection_template = self.oneview_client.connection_templates.get(ethernet_network['connectionTemplateUri']) + + merged_data = connection_template.copy() + merged_data.update({'bandwidth': bandwidth}) + + if not self.compare(connection_template, merged_data): + connection_template = self.oneview_client.connection_templates.update(merged_data) + return True, connection_template + else: + return False, None + + def _default_bandwidth_reset(self, resource): + + if not resource: + raise OneViewModuleResourceNotFound(self.MSG_ETHERNET_NETWORK_NOT_FOUND) + + default_connection_template = self.oneview_client.connection_templates.get_default() + + changed, connection_template = self._update_connection_template(resource, default_connection_template['bandwidth']) + + return changed, self.MSG_CONNECTION_TEMPLATE_RESET, dict( + ethernet_network_connection_template=connection_template) + + +def main(): + EthernetNetworkModule().run() + + +if __name__ == '__main__': + main() diff --git a/test/units/modules/remote_management/oneview/oneview_module_loader.py b/test/units/modules/remote_management/oneview/oneview_module_loader.py index 01d6340b05..e220cb8592 100644 --- a/test/units/modules/remote_management/oneview/oneview_module_loader.py +++ b/test/units/modules/remote_management/oneview/oneview_module_loader.py @@ -17,16 +17,18 @@ import sys from ansible.compat.tests.mock import patch, Mock + sys.modules['hpOneView'] = Mock() sys.modules['hpOneView.oneview_client'] = Mock() -sys.modules['hpOneView.exceptions'] = Mock() sys.modules['future'] = Mock() sys.modules['__future__'] = Mock() ONEVIEW_MODULE_UTILS_PATH = 'ansible.module_utils.oneview' -from ansible.module_utils.oneview import (HPOneViewException, - HPOneViewTaskError, +from ansible.module_utils.oneview import (OneViewModuleException, + OneViewModuleTaskError, + OneViewModuleResourceNotFound, OneViewModuleBase) +from ansible.modules.remote_management.oneview.oneview_ethernet_network import EthernetNetworkModule from ansible.modules.remote_management.oneview.oneview_fc_network import FcNetworkModule from ansible.modules.remote_management.oneview.oneview_fcoe_network import FcoeNetworkModule diff --git a/test/units/modules/remote_management/oneview/test_oneview_ethernet_network.py b/test/units/modules/remote_management/oneview/test_oneview_ethernet_network.py new file mode 100644 index 0000000000..01d3a1d477 --- /dev/null +++ b/test/units/modules/remote_management/oneview/test_oneview_ethernet_network.py @@ -0,0 +1,400 @@ +# -*- coding: utf-8 -*- +# +# Copyright (2016-2017) Hewlett Packard Enterprise Development LP +# +# This program 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. +# +# This program 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 this program. If not, see . + +import yaml + +from ansible.compat.tests import unittest, mock +from oneview_module_loader import EthernetNetworkModule, OneViewModuleResourceNotFound +from hpe_test_utils import OneViewBaseTestCase + +FAKE_MSG_ERROR = 'Fake message error' +DEFAULT_ETHERNET_NAME = 'Test Ethernet Network' +RENAMED_ETHERNET = 'Renamed Ethernet Network' + +DEFAULT_ENET_TEMPLATE = dict( + name=DEFAULT_ETHERNET_NAME, + vlanId=200, + ethernetNetworkType="Tagged", + purpose="General", + smartLink=False, + privateNetwork=False, + connectionTemplateUri=None +) + +PARAMS_FOR_PRESENT = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_ETHERNET_NAME) +) + +PARAMS_TO_RENAME = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_ETHERNET_NAME, + newName=RENAMED_ETHERNET) +) + +YAML_PARAMS_WITH_CHANGES = """ + config: "config.json" + state: present + data: + name: 'Test Ethernet Network' + purpose: Management + connectionTemplateUri: ~ + bandwidth: + maximumBandwidth: 3000 + typicalBandwidth: 2000 +""" + +YAML_RESET_CONNECTION_TEMPLATE = """ + config: "{{ config }}" + state: default_bandwidth_reset + data: + name: 'network name' +""" + +PARAMS_FOR_SCOPES_SET = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_ETHERNET_NAME) +) + +PARAMS_FOR_ABSENT = dict( + config='config.json', + state='absent', + data=dict(name=DEFAULT_ETHERNET_NAME) +) + +PARAMS_FOR_BULK_CREATED = dict( + config='config.json', + state='present', + data=dict(namePrefix="TestNetwork", vlanIdRange="1-2,5,9-10") +) + +DEFAULT_BULK_ENET_TEMPLATE = [ + {'name': 'TestNetwork_1', 'vlanId': 1}, + {'name': 'TestNetwork_2', 'vlanId': 2}, + {'name': 'TestNetwork_5', 'vlanId': 5}, + {'name': 'TestNetwork_9', 'vlanId': 9}, + {'name': 'TestNetwork_10', 'vlanId': 10}, +] + +DICT_PARAMS_WITH_CHANGES = yaml.load(YAML_PARAMS_WITH_CHANGES)["data"] + + +class EthernetNetworkModuleSpec(unittest.TestCase, + OneViewBaseTestCase): + """ + OneViewBaseTestCase provides the mocks used in this test case + """ + + def setUp(self): + self.configure_mocks(self, EthernetNetworkModule) + self.resource = self.mock_ov_client.ethernet_networks + + def test_should_create_new_ethernet_network(self): + self.resource.get_by.return_value = [] + self.resource.create.return_value = DEFAULT_ENET_TEMPLATE + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_CREATED, + ansible_facts=dict(ethernet_network=DEFAULT_ENET_TEMPLATE) + ) + + def test_should_not_update_when_data_is_equals(self): + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=EthernetNetworkModule.MSG_ALREADY_PRESENT, + ansible_facts=dict(ethernet_network=DEFAULT_ENET_TEMPLATE) + ) + + def test_update_when_data_has_modified_attributes(self): + data_merged = DEFAULT_ENET_TEMPLATE.copy() + data_merged['purpose'] = 'Management' + + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + self.resource.update.return_value = data_merged + self.mock_ov_client.connection_templates.get.return_value = {"uri": "uri"} + + self.mock_ansible_module.params = yaml.load(YAML_PARAMS_WITH_CHANGES) + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_UPDATED, + ansible_facts=dict(ethernet_network=data_merged) + ) + + def test_update_when_only_bandwidth_has_modified_attributes(self): + self.resource.get_by.return_value = [DICT_PARAMS_WITH_CHANGES] + self.mock_ov_client.connection_templates.get.return_value = {"uri": "uri"} + + self.mock_ansible_module.params = yaml.load(YAML_PARAMS_WITH_CHANGES) + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_UPDATED, + ansible_facts=dict(ethernet_network=DICT_PARAMS_WITH_CHANGES) + ) + + def test_update_when_data_has_modified_attributes_but_bandwidth_is_equal(self): + data_merged = DEFAULT_ENET_TEMPLATE.copy() + data_merged['purpose'] = 'Management' + + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + self.resource.update.return_value = data_merged + self.mock_ov_client.connection_templates.get.return_value = { + "bandwidth": DICT_PARAMS_WITH_CHANGES['bandwidth']} + + self.mock_ansible_module.params = yaml.load(YAML_PARAMS_WITH_CHANGES) + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_UPDATED, + ansible_facts=dict(ethernet_network=data_merged) + ) + + def test_update_successfully_even_when_connection_template_uri_not_exists(self): + data_merged = DEFAULT_ENET_TEMPLATE.copy() + del data_merged['connectionTemplateUri'] + + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + self.resource.update.return_value = data_merged + + self.mock_ansible_module.params = yaml.load(YAML_PARAMS_WITH_CHANGES) + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_UPDATED, + ansible_facts=dict(ethernet_network=data_merged) + ) + + def test_rename_when_resource_exists(self): + data_merged = DEFAULT_ENET_TEMPLATE.copy() + data_merged['name'] = RENAMED_ETHERNET + params_to_rename = PARAMS_TO_RENAME.copy() + + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + self.resource.update.return_value = data_merged + + self.mock_ansible_module.params = params_to_rename + + EthernetNetworkModule().run() + + self.resource.update.assert_called_once_with(data_merged) + + def test_create_with_new_name_when_resource_not_exists(self): + data_merged = DEFAULT_ENET_TEMPLATE.copy() + data_merged['name'] = RENAMED_ETHERNET + params_to_rename = PARAMS_TO_RENAME.copy() + + self.resource.get_by.return_value = [] + self.resource.create.return_value = DEFAULT_ENET_TEMPLATE + + self.mock_ansible_module.params = params_to_rename + + EthernetNetworkModule().run() + + self.resource.create.assert_called_once_with(PARAMS_TO_RENAME['data']) + + def test_should_remove_ethernet_network(self): + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_DELETED + ) + + def test_should_do_nothing_when_ethernet_network_not_exist(self): + self.resource.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=EthernetNetworkModule.MSG_ALREADY_ABSENT + ) + + def test_should_create_all_ethernet_networks(self): + self.resource.get_range.side_effect = [[], DEFAULT_BULK_ENET_TEMPLATE] + self.resource.create_bulk.return_value = DEFAULT_BULK_ENET_TEMPLATE + + self.mock_ansible_module.params = PARAMS_FOR_BULK_CREATED + + EthernetNetworkModule().run() + + self.resource.create_bulk.assert_called_once_with( + dict(namePrefix="TestNetwork", vlanIdRange="1-2,5,9-10")) + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_BULK_CREATED, + ansible_facts=dict(ethernet_network_bulk=DEFAULT_BULK_ENET_TEMPLATE)) + + def test_should_create_missing_ethernet_networks(self): + enet_get_range_return = [ + {'name': 'TestNetwork_1', 'vlanId': 1}, + {'name': 'TestNetwork_2', 'vlanId': 2}, + ] + + self.resource.get_range.side_effect = [enet_get_range_return, DEFAULT_BULK_ENET_TEMPLATE] + self.resource.dissociate_values_or_ranges.return_value = [1, 2, 5, 9, 10] + + self.mock_ansible_module.params = PARAMS_FOR_BULK_CREATED + + EthernetNetworkModule().run() + + self.resource.create_bulk.assert_called_once_with( + dict(namePrefix="TestNetwork", vlanIdRange="5,9,10")) + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, msg=EthernetNetworkModule.MSG_MISSING_BULK_CREATED, + ansible_facts=dict(ethernet_network_bulk=DEFAULT_BULK_ENET_TEMPLATE)) + + def test_should_create_missing_ethernet_networks_with_just_one_difference(self): + enet_get_range_return = [ + {'name': 'TestNetwork_1', 'vlanId': 1}, + {'name': 'TestNetwork_2', 'vlanId': 2}, + ] + + self.resource.get_range.side_effect = [enet_get_range_return, DEFAULT_BULK_ENET_TEMPLATE] + self.resource.dissociate_values_or_ranges.return_value = [1, 2, 5] + + self.mock_ansible_module.params = PARAMS_FOR_BULK_CREATED + + EthernetNetworkModule().run() + + self.resource.create_bulk.assert_called_once_with({'vlanIdRange': '5-5', 'namePrefix': 'TestNetwork'}) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_MISSING_BULK_CREATED, + ansible_facts=dict(ethernet_network_bulk=DEFAULT_BULK_ENET_TEMPLATE)) + + def test_should_do_nothing_when_ethernet_networks_already_exist(self): + self.resource.get_range.return_value = DEFAULT_BULK_ENET_TEMPLATE + self.resource.dissociate_values_or_ranges.return_value = [1, 2, 5, 9, 10] + + self.mock_ansible_module.params = PARAMS_FOR_BULK_CREATED + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, msg=EthernetNetworkModule.MSG_BULK_ALREADY_EXIST, + ansible_facts=dict(ethernet_network_bulk=DEFAULT_BULK_ENET_TEMPLATE)) + + def test_reset_successfully(self): + self.resource.get_by.return_value = [DICT_PARAMS_WITH_CHANGES] + self.mock_ov_client.connection_templates.update.return_value = {'result': 'success'} + self.mock_ov_client.connection_templates.get.return_value = { + "bandwidth": DICT_PARAMS_WITH_CHANGES['bandwidth']} + + self.mock_ov_client.connection_templates.get_default.return_value = {"bandwidth": { + "max": 1 + }} + + self.mock_ansible_module.params = yaml.load(YAML_RESET_CONNECTION_TEMPLATE) + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, msg=EthernetNetworkModule.MSG_CONNECTION_TEMPLATE_RESET, + ansible_facts=dict(ethernet_network_connection_template={'result': 'success'})) + + def test_should_fail_when_reset_not_existing_ethernet_network(self): + self.resource.get_by.return_value = [None] + + self.mock_ansible_module.params = yaml.load(YAML_RESET_CONNECTION_TEMPLATE) + + EthernetNetworkModule().run() + + self.mock_ansible_module.fail_json.assert_called_once_with( + exception=mock.ANY, + msg=EthernetNetworkModule.MSG_ETHERNET_NETWORK_NOT_FOUND + ) + + def test_update_scopes_when_different(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = DEFAULT_ENET_TEMPLATE.copy() + resource_data['scopeUris'] = ['fake'] + resource_data['uri'] = 'rest/ethernet/fake' + self.resource.get_by.return_value = [resource_data] + + patch_return = resource_data.copy() + patch_return['scopeUris'] = ['test'] + self.resource.patch.return_value = patch_return + + EthernetNetworkModule().run() + + self.resource.patch.assert_called_once_with('rest/ethernet/fake', + operation='replace', + path='/scopeUris', + value=['test']) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + ansible_facts=dict(ethernet_network=patch_return), + msg=EthernetNetworkModule.MSG_UPDATED + ) + + def test_should_do_nothing_when_scopes_are_the_same(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = DEFAULT_ENET_TEMPLATE.copy() + resource_data['scopeUris'] = ['test'] + self.resource.get_by.return_value = [resource_data] + + EthernetNetworkModule().run() + + self.resource.patch.not_been_called() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ansible_facts=dict(ethernet_network=resource_data), + msg=EthernetNetworkModule.MSG_ALREADY_PRESENT + ) + + +if __name__ == '__main__': + unittest.main()