New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial commits for integration of HPE OneView resources with Ansible #26026
Changes from all commits
5fd572d
7969abe
8af214c
b11506b
b474022
ce27f80
513c098
4da165f
a39a5bc
e783df0
171986a
40c6376
4872896
a6cdde8
a5e98bd
80a76c9
c8d3fdc
e36e64b
09142f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
#!/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.0', | ||
'status': ['preview'], | ||
'supported_by': 'community'} | ||
|
||
DOCUMENTATION = ''' | ||
--- | ||
module: oneview_fc_network | ||
short_description: Manage OneView Fibre Channel Network resources. | ||
description: | ||
- Provides an interface to manage Fibre Channel Network resources. Can create, update, and delete. | ||
version_added: "2.4" | ||
requirements: | ||
- "hpOneView >= 4.0.0" | ||
author: "Felipe Bulsoni (@fgbulsoni)" | ||
options: | ||
state: | ||
description: | ||
- Indicates the desired state for the Fibre Channel Network resource. | ||
C(present) will ensure data properties are compliant with OneView. | ||
C(absent) will remove the resource from OneView, if it exists. | ||
choices: ['present', 'absent'] | ||
data: | ||
description: | ||
- List with the Fibre Channel Network properties. | ||
required: true | ||
|
||
extends_documentation_fragment: | ||
- oneview | ||
- oneview.validateetag | ||
''' | ||
|
||
EXAMPLES = ''' | ||
- name: Ensure that the Fibre Channel Network is present using the default configuration | ||
oneview_fc_network: | ||
config: "{{ config_file_path }}" | ||
state: present | ||
data: | ||
name: 'New FC Network' | ||
|
||
- name: Ensure that the Fibre Channel Network is present with fabricType 'DirectAttach' | ||
oneview_fc_network: | ||
config: "{{ config_file_path }}" | ||
state: present | ||
data: | ||
name: 'New FC Network' | ||
fabricType: 'DirectAttach' | ||
|
||
- name: Ensure that the Fibre Channel Network is present and is inserted in the desired scopes | ||
oneview_fc_network: | ||
config: "{{ config_file_path }}" | ||
state: present | ||
data: | ||
name: 'New FC Network' | ||
scopeUris: | ||
- '/rest/scopes/00SC123456' | ||
- '/rest/scopes/01SC123456' | ||
|
||
- name: Ensure that the Fibre Channel Network is absent | ||
oneview_fc_network: | ||
config: "{{ config_file_path }}" | ||
state: absent | ||
data: | ||
name: 'New FC Network' | ||
''' | ||
|
||
RETURN = ''' | ||
fc_network: | ||
description: Has the facts about the managed OneView FC Network. | ||
returned: On state 'present'. Can be null. | ||
type: dict | ||
''' | ||
|
||
from ansible.module_utils.oneview import OneViewModuleBase | ||
|
||
|
||
class FcNetworkModule(OneViewModuleBase): | ||
MSG_CREATED = 'FC Network created successfully.' | ||
MSG_UPDATED = 'FC Network updated successfully.' | ||
MSG_DELETED = 'FC Network deleted successfully.' | ||
MSG_ALREADY_PRESENT = 'FC Network is already present.' | ||
MSG_ALREADY_ABSENT = 'FC Network is already absent.' | ||
RESOURCE_FACT_NAME = 'fc_network' | ||
|
||
def __init__(self): | ||
|
||
additional_arg_spec = dict(data=dict(required=True, type='dict'), | ||
state=dict( | ||
required=True, | ||
choices=['present', 'absent'])) | ||
|
||
super(FcNetworkModule, self).__init__(additional_arg_spec=additional_arg_spec, | ||
validate_etag_support=True) | ||
|
||
self.resource_client = self.oneview_client.fc_networks | ||
|
||
def execute_module(self): | ||
resource = self.get_by_name(self.data['name']) | ||
|
||
if self.state == 'present': | ||
return self._present(resource) | ||
else: | ||
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, 'fc_network', scope_uris) | ||
return result | ||
|
||
|
||
def main(): | ||
FcNetworkModule().run() | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# -*- 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 <http://www.gnu.org/licenses/>. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you want to, you can change this license header to the short, one line version as shown here: https://github.com/ansible/ansible/blob/devel/docs/docsite/rst/dev_guide/developing_modules_documenting.rst#copyright I've been trying to move modules to that since modules are copied over the network to the remote machine and so making them smaller can speed that up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so, in that link I see this short licence:
I'm assuming the oneview_fc_network.py will then have:
Correct? Also, since that licence is GNU and module_utils must be BSD, is there a similar model for BSD? |
||
|
||
|
||
class ModuleDocFragment(object): | ||
|
||
# OneView doc fragment | ||
DOCUMENTATION = ''' | ||
options: | ||
config: | ||
description: | ||
- Path to a .json configuration file containing the OneView client configuration. | ||
The configuration file is optional and when used should be present in the host running the ansible commands. | ||
If the file path is not provided, the configuration will be loaded from environment variables. | ||
For links to example configuration files or how to use the environment variables verify the notes section. | ||
required: false | ||
|
||
requirements: | ||
- "python >= 2.7.9" | ||
|
||
notes: | ||
- "A sample configuration file for the config parameter can be found at: | ||
U(https://github.com/HewlettPackard/oneview-ansible/blob/master/examples/oneview_config-rename.json)" | ||
- "Check how to use environment variables for configuration at: | ||
U(https://github.com/HewlettPackard/oneview-ansible#environment-variables)" | ||
- "Additional Playbooks for the HPE OneView Ansible modules can be found at: | ||
U(https://github.com/HewlettPackard/oneview-ansible/tree/master/examples)" | ||
''' | ||
|
||
VALIDATEETAG = ''' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this and the FACTSPARAMS variables do anything? I was unaware that the documentation generator would make use of anything besides DOCUMENTATION. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hello, it is apparently able to use it when extending documentation. I see this inside the FCNetworkModule:
And looking at the
And we also have facts modules which call the FACTSPARAMS for extending documentation, so these should be useful. |
||
options: | ||
validate_etag: | ||
description: | ||
- When the ETag Validation is enabled, the request will be conditionally processed only if the current ETag | ||
for the resource matches the ETag provided in the data. | ||
default: true | ||
choices: ['true', 'false'] | ||
''' | ||
|
||
FACTSPARAMS = ''' | ||
options: | ||
params: | ||
description: | ||
- List of params to delimit, filter and sort the list of resources. | ||
- "params allowed: | ||
C(start): The first item to return, using 0-based indexing. | ||
C(count): The number of resources to return. | ||
C(filter): A general filter/query string to narrow the list of items returned. | ||
C(sort): The sort order of the returned data set." | ||
required: false | ||
''' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
# -*- 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 <http://www.gnu.org/licenses/>. | ||
|
||
import yaml | ||
from mock import Mock, patch | ||
from oneview_module_loader import ONEVIEW_MODULE_UTILS_PATH | ||
from hpOneView.oneview_client import OneViewClient | ||
|
||
|
||
class OneViewBaseTestCase(object): | ||
mock_ov_client_from_json_file = None | ||
testing_class = None | ||
mock_ansible_module = None | ||
mock_ov_client = None | ||
testing_module = None | ||
EXAMPLES = None | ||
|
||
def configure_mocks(self, test_case, testing_class): | ||
""" | ||
Preload mocked OneViewClient instance and AnsibleModule | ||
Args: | ||
test_case (object): class instance (self) that are inheriting from OneViewBaseTestCase | ||
testing_class (object): class being tested | ||
""" | ||
self.testing_class = testing_class | ||
|
||
# Define OneView Client Mock (FILE) | ||
patcher_json_file = patch.object(OneViewClient, 'from_json_file') | ||
test_case.addCleanup(patcher_json_file.stop) | ||
self.mock_ov_client_from_json_file = patcher_json_file.start() | ||
|
||
# Define OneView Client Mock | ||
self.mock_ov_client = self.mock_ov_client_from_json_file.return_value | ||
|
||
# Define Ansible Module Mock | ||
patcher_ansible = patch(ONEVIEW_MODULE_UTILS_PATH + '.AnsibleModule') | ||
test_case.addCleanup(patcher_ansible.stop) | ||
mock_ansible_module = patcher_ansible.start() | ||
self.mock_ansible_module = Mock() | ||
mock_ansible_module.return_value = self.mock_ansible_module | ||
|
||
self.__set_module_examples() | ||
|
||
def test_main_function_should_call_run_method(self): | ||
self.mock_ansible_module.params = {'config': 'config.json'} | ||
|
||
main_func = getattr(self.testing_module, 'main') | ||
|
||
with patch.object(self.testing_class, "run") as mock_run: | ||
main_func() | ||
mock_run.assert_called_once() | ||
|
||
def __set_module_examples(self): | ||
# 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) | ||
|
||
try: | ||
# Load scenarios from module examples (Also checks if it is a valid yaml) | ||
self.EXAMPLES = yaml.load(self.testing_module.EXAMPLES, yaml.SafeLoader) | ||
|
||
except yaml.scanner.ScannerError: | ||
message = "Something went wrong while parsing yaml from {}.EXAMPLES".format(self.testing_class.__module__) | ||
raise Exception(message) | ||
|
||
|
||
class FactsParamsTestCase(OneViewBaseTestCase): | ||
""" | ||
FactsParamsTestCase has common test for classes that support pass additional | ||
parameters when retrieving all resources. | ||
""" | ||
|
||
def configure_client_mock(self, resorce_client): | ||
""" | ||
Args: | ||
resorce_client: Resource client that is being called | ||
""" | ||
self.resource_client = resorce_client | ||
|
||
def __validations(self): | ||
if not self.testing_class: | ||
raise Exception("Mocks are not configured, you must call 'configure_mocks' before running this test.") | ||
|
||
if not self.resource_client: | ||
raise Exception( | ||
"Mock for the client not configured, you must call 'configure_client_mock' before running this test.") | ||
|
||
def test_should_get_all_using_filters(self): | ||
self.__validations() | ||
self.resource_client.get_all.return_value = [] | ||
|
||
params_get_all_with_filters = dict( | ||
config='config.json', | ||
name=None, | ||
params={ | ||
'start': 1, | ||
'count': 3, | ||
'sort': 'name:descending', | ||
'filter': 'purpose=General', | ||
'query': 'imported eq true' | ||
}) | ||
self.mock_ansible_module.params = params_get_all_with_filters | ||
|
||
self.testing_class().run() | ||
|
||
self.resource_client.get_all.assert_called_once_with(start=1, count=3, sort='name:descending', | ||
filter='purpose=General', | ||
query='imported eq true') | ||
|
||
def test_should_get_all_without_params(self): | ||
self.__validations() | ||
self.resource_client.get_all.return_value = [] | ||
|
||
params_get_all_with_filters = dict( | ||
config='config.json', | ||
name=None | ||
) | ||
self.mock_ansible_module.params = params_get_all_with_filters | ||
|
||
self.testing_class().run() | ||
|
||
self.resource_client.get_all.assert_called_once_with() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# -*- 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 <http://www.gnu.org/licenses/>. | ||
|
||
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, | ||
OneViewModuleBase) | ||
|
||
from ansible.modules.remote_management.hpe.oneview_fc_network import FcNetworkModule |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jimi-c are we okay with modules that just have a free-form data parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jimi-c said on slack that this is fine.
We have added a new feature in 2.4 that allows specifying the structure of objects inside of a list if you want to do that. It could also be done as an update to the module. (I'm not entirely sure how to use it yet, knowledge of usage is still percolating out).