From 84005498bc22ba0c9cac358d0053dff0d8888d00 Mon Sep 17 00:00:00 2001 From: Felipe Garcia Bulsoni Date: Tue, 22 Aug 2017 17:14:10 -0300 Subject: [PATCH] Support Fibre Channel over Ethernet resource of HPE OneView (#28359) * Added new oneview_fcoe_network module and unit tests * Fixing metadata issues and importing unittest from ansible.compat.tests * Fixing shebang and adding correct copyright header * Renamed remote_management/hpe to remote_management/oneview * Updated documentation and default state according to review comments - Added present as the default state - Added delegate_to: localhost in all examples - Changed config path from a variable to illustrate a location - Other documentation fixes --- .../{hpe => oneview}/__init__.py | 0 .../{hpe => oneview}/oneview_fc_network.py | 0 .../oneview/oneview_fcoe_network.py | 121 ++++++++++++ .../{hpe => oneview}/hpe_test_utils.py | 2 +- .../{hpe => oneview}/oneview_module_loader.py | 3 +- .../test_oneview_fc_network.py | 0 .../oneview/test_oneview_fcoe_network.py | 176 ++++++++++++++++++ 7 files changed, 300 insertions(+), 2 deletions(-) rename lib/ansible/modules/remote_management/{hpe => oneview}/__init__.py (100%) rename lib/ansible/modules/remote_management/{hpe => oneview}/oneview_fc_network.py (100%) create mode 100644 lib/ansible/modules/remote_management/oneview/oneview_fcoe_network.py rename test/units/modules/remote_management/{hpe => oneview}/hpe_test_utils.py (99%) rename test/units/modules/remote_management/{hpe => oneview}/oneview_module_loader.py (87%) rename test/units/modules/remote_management/{hpe => oneview}/test_oneview_fc_network.py (100%) create mode 100644 test/units/modules/remote_management/oneview/test_oneview_fcoe_network.py diff --git a/lib/ansible/modules/remote_management/hpe/__init__.py b/lib/ansible/modules/remote_management/oneview/__init__.py similarity index 100% rename from lib/ansible/modules/remote_management/hpe/__init__.py rename to lib/ansible/modules/remote_management/oneview/__init__.py diff --git a/lib/ansible/modules/remote_management/hpe/oneview_fc_network.py b/lib/ansible/modules/remote_management/oneview/oneview_fc_network.py similarity index 100% rename from lib/ansible/modules/remote_management/hpe/oneview_fc_network.py rename to lib/ansible/modules/remote_management/oneview/oneview_fc_network.py diff --git a/lib/ansible/modules/remote_management/oneview/oneview_fcoe_network.py b/lib/ansible/modules/remote_management/oneview/oneview_fcoe_network.py new file mode 100644 index 0000000000..61189706ea --- /dev/null +++ b/lib/ansible/modules/remote_management/oneview/oneview_fcoe_network.py @@ -0,0 +1,121 @@ +#!/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_fcoe_network +short_description: Manage OneView FCoE Network resources +description: + - Provides an interface to manage FCoE Network resources. Can create, update, or delete. +version_added: "2.4" +requirements: + - "python >= 2.7.9" + - "hpOneView >= 4.0.0" +author: "Felipe Bulsoni (@fgbulsoni)" +options: + state: + description: + - Indicates the desired state for the FCoE Network 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 FCoE Network properties. + required: true + +extends_documentation_fragment: + - oneview + - oneview.validateetag +''' + +EXAMPLES = ''' +- name: Ensure that FCoE Network is present using the default configuration + oneview_fcoe_network: + config: '/etc/oneview/oneview_config.json' + state: present + data: + name: Test FCoE Network + vlanId: 201 + delegate_to: localhost + +- name: Update the FCOE network scopes + oneview_fcoe_network: + config: '/etc/oneview/oneview_config.json' + state: present + data: + name: New FCoE Network + scopeUris: + - '/rest/scopes/00SC123456' + - '/rest/scopes/01SC123456' + delegate_to: localhost + +- name: Ensure that FCoE Network is absent + oneview_fcoe_network: + config: '/etc/oneview/oneview_config.json' + state: absent + data: + name: New FCoE Network + delegate_to: localhost +''' + +RETURN = ''' +fcoe_network: + description: Has the facts about the OneView FCoE Networks. + returned: On state 'present'. Can be null. + type: dict +''' + +from ansible.module_utils.oneview import OneViewModuleBase + + +class FcoeNetworkModule(OneViewModuleBase): + MSG_CREATED = 'FCoE Network created successfully.' + MSG_UPDATED = 'FCoE Network updated successfully.' + MSG_DELETED = 'FCoE Network deleted successfully.' + MSG_ALREADY_PRESENT = 'FCoE Network is already present.' + MSG_ALREADY_ABSENT = 'FCoE Network is already absent.' + RESOURCE_FACT_NAME = 'fcoe_network' + + def __init__(self): + + additional_arg_spec = dict(data=dict(required=True, type='dict'), + state=dict(default='present', + choices=['present', 'absent'])) + + super(FcoeNetworkModule, self).__init__(additional_arg_spec=additional_arg_spec, + validate_etag_support=True) + + self.resource_client = self.oneview_client.fcoe_networks + + 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) + result = self.resource_present(resource, self.RESOURCE_FACT_NAME) + if scope_uris is not None: + result = self.resource_scopes_set(result, 'fcoe_network', scope_uris) + return result + + +def main(): + FcoeNetworkModule().run() + + +if __name__ == '__main__': + main() diff --git a/test/units/modules/remote_management/hpe/hpe_test_utils.py b/test/units/modules/remote_management/oneview/hpe_test_utils.py similarity index 99% rename from test/units/modules/remote_management/hpe/hpe_test_utils.py rename to test/units/modules/remote_management/oneview/hpe_test_utils.py index 4e2d53a13d..620c1ea656 100644 --- a/test/units/modules/remote_management/hpe/hpe_test_utils.py +++ b/test/units/modules/remote_management/oneview/hpe_test_utils.py @@ -68,7 +68,7 @@ class OneViewBaseTestCase(object): # Load scenarios from module examples (Also checks if it is a valid yaml) ansible = __import__('ansible') testing_module = self.testing_class.__module__.split('.')[-1] - self.testing_module = getattr(ansible.modules.remote_management.hpe, testing_module) + self.testing_module = getattr(ansible.modules.remote_management.oneview, testing_module) try: # Load scenarios from module examples (Also checks if it is a valid yaml) diff --git a/test/units/modules/remote_management/hpe/oneview_module_loader.py b/test/units/modules/remote_management/oneview/oneview_module_loader.py similarity index 87% rename from test/units/modules/remote_management/hpe/oneview_module_loader.py rename to test/units/modules/remote_management/oneview/oneview_module_loader.py index dfbc027287..01d6340b05 100644 --- a/test/units/modules/remote_management/hpe/oneview_module_loader.py +++ b/test/units/modules/remote_management/oneview/oneview_module_loader.py @@ -28,4 +28,5 @@ from ansible.module_utils.oneview import (HPOneViewException, HPOneViewTaskError, OneViewModuleBase) -from ansible.modules.remote_management.hpe.oneview_fc_network import FcNetworkModule +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/hpe/test_oneview_fc_network.py b/test/units/modules/remote_management/oneview/test_oneview_fc_network.py similarity index 100% rename from test/units/modules/remote_management/hpe/test_oneview_fc_network.py rename to test/units/modules/remote_management/oneview/test_oneview_fc_network.py diff --git a/test/units/modules/remote_management/oneview/test_oneview_fcoe_network.py b/test/units/modules/remote_management/oneview/test_oneview_fcoe_network.py new file mode 100644 index 0000000000..333576c236 --- /dev/null +++ b/test/units/modules/remote_management/oneview/test_oneview_fcoe_network.py @@ -0,0 +1,176 @@ +# -*- 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 . + +from ansible.compat.tests import unittest +from oneview_module_loader import FcoeNetworkModule +from hpe_test_utils import OneViewBaseTestCase + +FAKE_MSG_ERROR = 'Fake message error' + +DEFAULT_FCOE_NETWORK_TEMPLATE = dict( + name='New FCoE Network 2', + vlanId="201", + connectionTemplateUri=None +) + +PARAMS_FOR_PRESENT = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_FCOE_NETWORK_TEMPLATE['name']) +) + +PARAMS_WITH_CHANGES = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_FCOE_NETWORK_TEMPLATE['name'], + fabricType='DirectAttach', + newName='New Name') +) + +PARAMS_FOR_ABSENT = dict( + config='config.json', + state='absent', + data=dict(name=DEFAULT_FCOE_NETWORK_TEMPLATE['name']) +) + + +class FcoeNetworkSpec(unittest.TestCase, + OneViewBaseTestCase): + """ + OneViewBaseTestCase provides the mocks used in this test case + """ + + def setUp(self): + self.configure_mocks(self, FcoeNetworkModule) + self.resource = self.mock_ov_client.fcoe_networks + + def test_should_create_new_fcoe_network(self): + self.resource.get_by.return_value = [] + self.resource.create.return_value = DEFAULT_FCOE_NETWORK_TEMPLATE + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + FcoeNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=FcoeNetworkModule.MSG_CREATED, + ansible_facts=dict(fcoe_network=DEFAULT_FCOE_NETWORK_TEMPLATE) + ) + + def test_should_not_update_when_data_is_equals(self): + self.resource.get_by.return_value = [DEFAULT_FCOE_NETWORK_TEMPLATE] + self.mock_ansible_module.params = PARAMS_FOR_PRESENT.copy() + + FcoeNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=FcoeNetworkModule.MSG_ALREADY_PRESENT, + ansible_facts=dict(fcoe_network=DEFAULT_FCOE_NETWORK_TEMPLATE) + ) + + def test_update_when_data_has_modified_attributes(self): + data_merged = DEFAULT_FCOE_NETWORK_TEMPLATE.copy() + data_merged['fabricType'] = 'DirectAttach' + + self.resource.get_by.return_value = [DEFAULT_FCOE_NETWORK_TEMPLATE] + self.resource.update.return_value = data_merged + + self.mock_ansible_module.params = PARAMS_WITH_CHANGES + + FcoeNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=FcoeNetworkModule.MSG_UPDATED, + ansible_facts=dict(fcoe_network=data_merged) + ) + + def test_should_remove_fcoe_network(self): + self.resource.get_by.return_value = [DEFAULT_FCOE_NETWORK_TEMPLATE] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + FcoeNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=FcoeNetworkModule.MSG_DELETED + ) + + def test_should_do_nothing_when_fcoe_network_not_exist(self): + self.resource.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + FcoeNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=FcoeNetworkModule.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 = DEFAULT_FCOE_NETWORK_TEMPLATE.copy() + resource_data['scopeUris'] = ['fake'] + resource_data['uri'] = 'rest/fcoe/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 + + FcoeNetworkModule().run() + + self.resource.patch.assert_called_once_with('rest/fcoe/fake', + operation='replace', + path='/scopeUris', + value=['test']) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + ansible_facts=dict(fcoe_network=patch_return), + msg=FcoeNetworkModule.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_FCOE_NETWORK_TEMPLATE.copy() + resource_data['scopeUris'] = ['test'] + self.resource.get_by.return_value = [resource_data] + + FcoeNetworkModule().run() + + self.resource.patch.not_been_called() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ansible_facts=dict(fcoe_network=resource_data), + msg=FcoeNetworkModule.MSG_ALREADY_PRESENT + ) + + +if __name__ == '__main__': + unittest.main()