diff --git a/lib/ansible/modules/remote_management/oneview/oneview_network_set.py b/lib/ansible/modules/remote_management/oneview/oneview_network_set.py new file mode 100644 index 00000000000..21c4ab8fce3 --- /dev/null +++ b/lib/ansible/modules/remote_management/oneview/oneview_network_set.py @@ -0,0 +1,154 @@ +#!/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_network_set +short_description: Manage HPE OneView Network Set resources +description: + - Provides an interface to manage Network Set resources. Can create, update, or delete. +version_added: "2.4" +requirements: + - hpOneView >= 4.0.0 +author: + - Felipe Bulsoni (@fgbulsoni) + - Thiago Miotto (@tmiotto) + - Adriane Cardozo (@adriane-cardozo) +options: + state: + description: + - Indicates the desired state for the Network Set resource. + - C(present) will ensure data properties are compliant with OneView. + - C(absent) will remove the resource from OneView, if it exists. + default: present + choices: ['present', 'absent'] + data: + description: + - List with the Network Set properties. + required: true + +extends_documentation_fragment: + - oneview + - oneview.validateetag +''' + +EXAMPLES = ''' +- name: Create a Network Set + oneview_network_set: + config: /etc/oneview/oneview_config.json + state: present + data: + name: OneViewSDK Test Network Set + networkUris: + - Test Ethernet Network_1 # can be a name + - /rest/ethernet-networks/e4360c9d-051d-4931-b2aa-7de846450dd8 # or a URI + delegate_to: localhost + +- name: Update the Network Set name to 'OneViewSDK Test Network Set - Renamed' and change the associated networks + oneview_network_set: + config: /etc/oneview/oneview_config.json + state: present + data: + name: OneViewSDK Test Network Set + newName: OneViewSDK Test Network Set - Renamed + networkUris: + - Test Ethernet Network_1 + delegate_to: localhost + +- name: Delete the Network Set + oneview_network_set: + config: /etc/oneview/oneview_config.json + state: absent + data: + name: OneViewSDK Test Network Set - Renamed + delegate_to: localhost + +- name: Update the Network set with two scopes + oneview_network_set: + config: /etc/oneview/oneview_config.json + state: present + data: + name: OneViewSDK Test Network Set + scopeUris: + - /rest/scopes/01SC123456 + - /rest/scopes/02SC123456 + delegate_to: localhost +''' + +RETURN = ''' +network_set: + description: Has the facts about the Network Set. + returned: On state 'present', but can be null. + type: dict +''' + +from ansible.module_utils.oneview import OneViewModuleBase, OneViewModuleResourceNotFound + + +class NetworkSetModule(OneViewModuleBase): + MSG_CREATED = 'Network Set created successfully.' + MSG_UPDATED = 'Network Set updated successfully.' + MSG_DELETED = 'Network Set deleted successfully.' + MSG_ALREADY_PRESENT = 'Network Set is already present.' + MSG_ALREADY_ABSENT = 'Network Set is already absent.' + MSG_ETHERNET_NETWORK_NOT_FOUND = 'Ethernet Network not found: ' + RESOURCE_FACT_NAME = 'network_set' + + argument_spec = dict( + state=dict(default='present', choices=['present', 'absent']), + data=dict(required=True, type='dict')) + + def __init__(self): + super(NetworkSetModule, self).__init__(additional_arg_spec=self.argument_spec, + validate_etag_support=True) + self.resource_client = self.oneview_client.network_sets + + def execute_module(self): + resource = self.get_by_name(self.data.get('name')) + + if self.state == 'present': + return self._present(resource) + elif self.state == 'absent': + return self.resource_absent(resource) + + def _present(self, resource): + scope_uris = self.data.pop('scopeUris', None) + self._replace_network_name_by_uri(self.data) + result = self.resource_present(resource, self.RESOURCE_FACT_NAME) + if scope_uris is not None: + result = self.resource_scopes_set(result, self.RESOURCE_FACT_NAME, scope_uris) + return result + + def _get_ethernet_network_by_name(self, name): + result = self.oneview_client.ethernet_networks.get_by('name', name) + return result[0] if result else None + + def _get_network_uri(self, network_name_or_uri): + if network_name_or_uri.startswith('/rest/ethernet-networks'): + return network_name_or_uri + else: + enet_network = self._get_ethernet_network_by_name(network_name_or_uri) + if enet_network: + return enet_network['uri'] + else: + raise OneViewModuleResourceNotFound(self.MSG_ETHERNET_NETWORK_NOT_FOUND + network_name_or_uri) + + def _replace_network_name_by_uri(self, data): + if 'networkUris' in data: + data['networkUris'] = [self._get_network_uri(x) for x in data['networkUris']] + + +def main(): + NetworkSetModule().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 eae77b1497f..c448b69c1d1 100644 --- a/test/units/modules/remote_management/oneview/oneview_module_loader.py +++ b/test/units/modules/remote_management/oneview/oneview_module_loader.py @@ -33,3 +33,4 @@ from ansible.modules.remote_management.oneview.oneview_ethernet_network import E from ansible.modules.remote_management.oneview.oneview_fc_network import FcNetworkModule from ansible.modules.remote_management.oneview.oneview_fc_network_facts import FcNetworkFactsModule from ansible.modules.remote_management.oneview.oneview_fcoe_network import FcoeNetworkModule +from ansible.modules.remote_management.oneview.oneview_network_set import NetworkSetModule diff --git a/test/units/modules/remote_management/oneview/test_oneview_network_set.py b/test/units/modules/remote_management/oneview/test_oneview_network_set.py new file mode 100644 index 00000000000..ff381c63f57 --- /dev/null +++ b/test/units/modules/remote_management/oneview/test_oneview_network_set.py @@ -0,0 +1,183 @@ +# 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 ansible.compat.tests import unittest, mock +from hpe_test_utils import OneViewBaseTestCase +from oneview_module_loader import NetworkSetModule + +FAKE_MSG_ERROR = 'Fake message error' + +NETWORK_SET = dict( + name='OneViewSDK Test Network Set', + networkUris=['/rest/ethernet-networks/aaa-bbb-ccc'] +) + +NETWORK_SET_WITH_NEW_NAME = dict(name='OneViewSDK Test Network Set - Renamed') + +PARAMS_FOR_PRESENT = dict( + config='config.json', + state='present', + data=dict(name=NETWORK_SET['name'], + networkUris=['/rest/ethernet-networks/aaa-bbb-ccc']) +) + +PARAMS_WITH_CHANGES = dict( + config='config.json', + state='present', + data=dict(name=NETWORK_SET['name'], + newName=NETWORK_SET['name'] + " - Renamed", + networkUris=['/rest/ethernet-networks/aaa-bbb-ccc', 'Name of a Network']) +) + +PARAMS_FOR_ABSENT = dict( + config='config.json', + state='absent', + data=dict(name=NETWORK_SET['name']) +) + + +class NetworkSetModuleSpec(unittest.TestCase, + OneViewBaseTestCase): + """ + OneViewBaseTestCase has common tests for class constructor and main function, + also provides the mocks used in this test case. + """ + + def setUp(self): + self.configure_mocks(self, NetworkSetModule) + self.resource = self.mock_ov_client.network_sets + self.ethernet_network_client = self.mock_ov_client.ethernet_networks + + def test_should_create_new_network_set(self): + self.resource.get_by.return_value = [] + self.resource.create.return_value = NETWORK_SET + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + NetworkSetModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=NetworkSetModule.MSG_CREATED, + ansible_facts=dict(network_set=NETWORK_SET) + ) + + def test_should_not_update_when_data_is_equals(self): + self.resource.get_by.return_value = [NETWORK_SET] + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + NetworkSetModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=NetworkSetModule.MSG_ALREADY_PRESENT, + ansible_facts=dict(network_set=NETWORK_SET) + ) + + def test_update_when_data_has_modified_attributes(self): + data_merged = dict(name=NETWORK_SET['name'] + " - Renamed", + networkUris=['/rest/ethernet-networks/aaa-bbb-ccc', + '/rest/ethernet-networks/ddd-eee-fff'] + ) + + self.resource.get_by.side_effect = [NETWORK_SET], [] + self.resource.update.return_value = data_merged + self.ethernet_network_client.get_by.return_value = [{'uri': '/rest/ethernet-networks/ddd-eee-fff'}] + + self.mock_ansible_module.params = PARAMS_WITH_CHANGES + + NetworkSetModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=NetworkSetModule.MSG_UPDATED, + ansible_facts=dict(network_set=data_merged) + ) + + def test_should_raise_exception_when_ethernet_network_not_found(self): + self.resource.get_by.side_effect = [NETWORK_SET], [] + self.ethernet_network_client.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_WITH_CHANGES + + NetworkSetModule().run() + + self.mock_ansible_module.fail_json.assert_called_once_with( + exception=mock.ANY, + msg=NetworkSetModule.MSG_ETHERNET_NETWORK_NOT_FOUND + "Name of a Network" + ) + + def test_should_remove_network(self): + self.resource.get_by.return_value = [NETWORK_SET] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + NetworkSetModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=NetworkSetModule.MSG_DELETED + ) + + def test_should_do_nothing_when_network_set_not_exist(self): + self.resource.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + NetworkSetModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=NetworkSetModule.MSG_ALREADY_ABSENT + ) + + 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 = NETWORK_SET.copy() + resource_data['scopeUris'] = ['fake'] + resource_data['uri'] = 'rest/network-sets/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 + + NetworkSetModule().run() + + self.resource.patch.assert_called_once_with('rest/network-sets/fake', + operation='replace', + path='/scopeUris', + value=['test']) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + ansible_facts=dict(network_set=patch_return), + msg=NetworkSetModule.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 = NETWORK_SET.copy() + resource_data['scopeUris'] = ['test'] + self.resource.get_by.return_value = [resource_data] + + NetworkSetModule().run() + + self.resource.patch.not_been_called() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ansible_facts=dict(network_set=resource_data), + msg=NetworkSetModule.MSG_ALREADY_PRESENT + ) + + +if __name__ == '__main__': + unittest.main()