forked from ansible/ansible
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New module: cvs snapshots for aws (ansible#61023)
* New module: cvs snapshots for aws * fixeS
- Loading branch information
1 parent
3bc5957
commit 799b0fa
Showing
2 changed files
with
390 additions
and
0 deletions.
There are no files selected for viewing
246 changes: 246 additions & 0 deletions
246
lib/ansible/modules/cloud/amazon/aws_netapp_cvs_snapshots.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
#!/usr/bin/python | ||
|
||
# (c) 2019, NetApp Inc | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
"""AWS Cloud Volumes Services - Manage Snapshots""" | ||
|
||
from __future__ import absolute_import, division, print_function | ||
|
||
__metaclass__ = type | ||
|
||
|
||
ANSIBLE_METADATA = {'metadata_version': '1.1', | ||
'status': ['preview'], | ||
'supported_by': 'community'} | ||
|
||
|
||
DOCUMENTATION = ''' | ||
module: aws_netapp_cvs_snapshots | ||
short_description: NetApp AWS Cloud Volumes Service Manage Snapshots. | ||
extends_documentation_fragment: | ||
- netapp.awscvs | ||
version_added: '2.9' | ||
author: NetApp Ansible Team (@carchi8py) <[email protected]> | ||
description: | ||
- Create, Update, Delete Snapshot on AWS Cloud Volumes Service. | ||
options: | ||
state: | ||
description: | ||
- Whether the specified snapshot should exist or not. | ||
required: true | ||
type: str | ||
choices: ['present', 'absent'] | ||
region: | ||
description: | ||
- The region to which the snapshot belongs to. | ||
required: true | ||
type: str | ||
name: | ||
description: | ||
- Name of the snapshot | ||
required: true | ||
type: str | ||
fileSystemId: | ||
description: | ||
- Name or Id of the filesystem. | ||
- Required for create operation | ||
type: str | ||
from_name: | ||
description: | ||
- ID or Name of the snapshot to rename. | ||
- Required to create an snapshot called 'name' by renaming 'from_name'. | ||
type: str | ||
''' | ||
|
||
EXAMPLES = """ | ||
- name: Create Snapshot | ||
aws_netapp_cvs_snapshots: | ||
state: present | ||
region: us-east-1 | ||
name: testSnapshot | ||
fileSystemId: testVolume | ||
api_url : cds-aws-bundles.netapp.com | ||
api_key: myApiKey | ||
secret_key : mySecretKey | ||
- name: Update Snapshot | ||
aws_netapp_cvs_snapshots: | ||
state: present | ||
region: us-east-1 | ||
name: testSnapshot - renamed | ||
from_name: testSnapshot | ||
fileSystemId: testVolume | ||
api_url : cds-aws-bundles.netapp.com | ||
api_key: myApiKey | ||
secret_key : mySecretKey | ||
- name: Delete Snapshot | ||
aws_netapp_cvs_snapshots: | ||
state: absent | ||
region: us-east-1 | ||
name: testSnapshot | ||
api_url : cds-aws-bundles.netapp.com | ||
api_key: myApiKey | ||
secret_key : mySecretKey | ||
""" | ||
|
||
RETURN = """ | ||
""" | ||
|
||
import ansible.module_utils.netapp as netapp_utils | ||
from ansible.module_utils.basic import AnsibleModule | ||
from ansible.module_utils.netapp_module import NetAppModule | ||
from ansible.module_utils.netapp import AwsCvsRestAPI | ||
|
||
|
||
class AwsCvsNetappSnapshot(object): | ||
""" | ||
Contains methods to parse arguments, | ||
derive details of AWS_CVS objects | ||
and send requests to AWS CVS via | ||
the restApi | ||
""" | ||
|
||
def __init__(self): | ||
""" | ||
Parse arguments, setup state variables, | ||
check paramenters and ensure request module is installed | ||
""" | ||
self.argument_spec = netapp_utils.aws_cvs_host_argument_spec() | ||
self.argument_spec.update(dict( | ||
state=dict(required=True, choices=['present', 'absent']), | ||
region=dict(required=True, type='str'), | ||
name=dict(required=True, type='str'), | ||
from_name=dict(required=False, type='str'), | ||
fileSystemId=dict(required=False, type='str') | ||
)) | ||
|
||
self.module = AnsibleModule( | ||
argument_spec=self.argument_spec, | ||
required_if=[ | ||
('state', 'present', ['state', 'name', 'fileSystemId']), | ||
], | ||
supports_check_mode=True | ||
) | ||
|
||
self.na_helper = NetAppModule() | ||
|
||
# set up state variables | ||
self.parameters = self.na_helper.set_parameters(self.module.params) | ||
# Calling generic AWSCVS restApi class | ||
self.restApi = AwsCvsRestAPI(self.module) | ||
|
||
# Checking for the parameters passed and create new parameters list | ||
self.data = {} | ||
for key in self.parameters.keys(): | ||
self.data[key] = self.parameters[key] | ||
|
||
def getSnapshotId(self, name): | ||
# Check if snapshot exists | ||
# Return snpashot Id If Snapshot is found, None otherwise | ||
list_snapshots, error = self.restApi.get('Snapshots') | ||
|
||
if error: | ||
self.module.fail_json(msg=error) | ||
|
||
for snapshot in list_snapshots: | ||
if snapshot['name'] == name: | ||
return snapshot['snapshotId'] | ||
return None | ||
|
||
def getfilesystemId(self): | ||
# Check given FileSystem is exists | ||
# Return fileSystemId is found, None otherwise | ||
list_filesystem, error = self.restApi.get('FileSystems') | ||
|
||
if error: | ||
self.module.fail_json(msg=error) | ||
for FileSystem in list_filesystem: | ||
if FileSystem['fileSystemId'] == self.parameters['fileSystemId']: | ||
return FileSystem['fileSystemId'] | ||
elif FileSystem['creationToken'] == self.parameters['fileSystemId']: | ||
return FileSystem['fileSystemId'] | ||
return None | ||
|
||
def create_snapshot(self): | ||
# Create Snapshot | ||
api = 'Snapshots' | ||
response, error = self.restApi.post(api, self.data) | ||
if error: | ||
self.module.fail_json(msg=error) | ||
|
||
def rename_snapshot(self, snapshotId): | ||
# Rename Snapshot | ||
api = 'Snapshots/' + snapshotId | ||
response, error = self.restApi.put(api, self.data) | ||
if error: | ||
self.module.fail_json(msg=error) | ||
|
||
def delete_snapshot(self, snapshotId): | ||
# Delete Snapshot | ||
api = 'Snapshots/' + snapshotId | ||
data = None | ||
response, error = self.restApi.delete(api, self.data) | ||
if error: | ||
self.module.fail_json(msg=error) | ||
|
||
def apply(self): | ||
""" | ||
Perform pre-checks, call functions and exit | ||
""" | ||
self.snapshotId = self.getSnapshotId(self.data['name']) | ||
|
||
if self.snapshotId is None and 'fileSystemId' in self.data: | ||
self.fileSystemId = self.getfilesystemId() | ||
self.data['fileSystemId'] = self.fileSystemId | ||
if self.fileSystemId is None: | ||
self.module.fail_json(msg='Error: Specified filesystem id %s does not exist ' % self.data['fileSystemId']) | ||
|
||
cd_action = self.na_helper.get_cd_action(self.snapshotId, self.data) | ||
result_message = "" | ||
if self.na_helper.changed: | ||
if self.module.check_mode: | ||
# Skip changes | ||
result_message = "Check mode, skipping changes" | ||
else: | ||
if cd_action == "delete": | ||
self.delete_snapshot(self.snapshotId) | ||
result_message = "Snapshot Deleted" | ||
|
||
elif cd_action == "create": | ||
if 'from_name' in self.data: | ||
# If cd_action is craete and from_name is given | ||
snapshotId = self.getSnapshotId(self.data['from_name']) | ||
if snapshotId is not None: | ||
# If resource pointed by from_name exists, rename the snapshot to name | ||
self.rename_snapshot(snapshotId) | ||
result_message = "Snapshot Updated" | ||
else: | ||
# If resource pointed by from_name does not exists, error out | ||
self.module.fail_json(msg="Resource does not exist : %s" % self.data['from_name']) | ||
else: | ||
self.create_snapshot() | ||
# If from_name is not defined, Create from scratch. | ||
result_message = "Snapshot Created" | ||
|
||
self.module.exit_json(changed=self.na_helper.changed, msg=result_message) | ||
|
||
|
||
def main(): | ||
""" | ||
Main function | ||
""" | ||
aws_netapp_cvs_snapshots = AwsCvsNetappSnapshot() | ||
aws_netapp_cvs_snapshots.apply() | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
144 changes: 144 additions & 0 deletions
144
test/units/modules/cloud/amazon/test_aws_netapp_cvs_snapshots.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
# (c) 2019, NetApp, Inc | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
''' unit tests ONTAP Ansible module: aws_netapp_cvs_snapshots''' | ||
|
||
from __future__ import (absolute_import, division, print_function) | ||
__metaclass__ = type | ||
import json | ||
import pytest | ||
|
||
|
||
from units.compat import unittest | ||
from units.compat.mock import patch, Mock | ||
from ansible.module_utils import basic | ||
from ansible.module_utils._text import to_bytes | ||
from requests import Response | ||
from ansible.modules.cloud.amazon.aws_netapp_cvs_snapshots \ | ||
import AwsCvsNetappSnapshot as snapshot_module | ||
|
||
|
||
def set_module_args(args): | ||
"""prepare arguments so that they will be picked up during module creation""" | ||
args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) | ||
basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access | ||
|
||
|
||
class AnsibleExitJson(Exception): | ||
"""Exception class to be raised by module.exit_json and caught by the test case""" | ||
pass | ||
|
||
|
||
class AnsibleFailJson(Exception): | ||
"""Exception class to be raised by module.fail_json and caught by the test case""" | ||
pass | ||
|
||
|
||
def exit_json(*args, **kwargs): # pylint: disable=unused-argument | ||
"""function to patch over exit_json; package return data into an exception""" | ||
if 'changed' not in kwargs: | ||
kwargs['changed'] = False | ||
raise AnsibleExitJson(kwargs) | ||
|
||
|
||
def fail_json(*args, **kwargs): # pylint: disable=unused-argument | ||
"""function to patch over fail_json; package return data into an exception""" | ||
kwargs['failed'] = True | ||
raise AnsibleFailJson(kwargs) | ||
|
||
|
||
class TestMyModule(unittest.TestCase): | ||
''' a group of related Unit Tests ''' | ||
|
||
def setUp(self): | ||
self.mock_module_helper = patch.multiple(basic.AnsibleModule, | ||
exit_json=exit_json, | ||
fail_json=fail_json) | ||
self.mock_module_helper.start() | ||
self.addCleanup(self.mock_module_helper.stop) | ||
|
||
def set_default_args_fail_check(self): | ||
return dict({ | ||
'name': 'TestFilesystem', | ||
'fileSystemId': 'standard', | ||
'from_name': 'from_TestFilesystem', | ||
'region': 'us-east-1', | ||
'api_url': 'hostname.com', | ||
'api_key': 'myapikey', | ||
'secret_key': 'mysecretkey' | ||
}) | ||
|
||
def set_default_args_pass_check(self): | ||
return dict({ | ||
'state': 'present', | ||
'name': 'testSnapshot', | ||
'fileSystemId': 'standard', | ||
'from_name': 'from_TestFilesystem', | ||
'region': 'us-east-1', | ||
'api_url': 'hostname.com', | ||
'api_key': 'myapikey', | ||
'secret_key': 'mysecretkey' | ||
}) | ||
|
||
def set_args_create_aws_netapp_cvs_snapshots(self): | ||
return dict({ | ||
'state': 'present', | ||
'name': 'testSnapshot', | ||
'fileSystemId': '123-4213-432-432', | ||
'region': 'us-east-1', | ||
'api_url': 'hostname.com', | ||
'api_key': 'myapikey', | ||
'secret_key': 'mysecretkey' | ||
}) | ||
|
||
def set_args_delete_aws_netapp_cvs_snapshots(self): | ||
return dict({ | ||
'state': 'absent', | ||
'name': 'testSnapshot', | ||
'region': 'us-east-1', | ||
'api_url': 'hostname.com', | ||
'api_key': 'myapikey', | ||
'secret_key': 'mysecretkey' | ||
}) | ||
|
||
def test_module_fail_when_required_args_missing(self): | ||
''' required arguments are reported as errors ''' | ||
with pytest.raises(AnsibleFailJson) as exc: | ||
set_module_args(self.set_default_args_fail_check()) | ||
snapshot_module() | ||
print('Info: test_module_fail_when_required_args_missing: %s' % exc.value.args[0]['msg']) | ||
|
||
def test_module_fail_when_required_args_present(self): | ||
''' required arguments are reported as errors ''' | ||
with pytest.raises(AnsibleExitJson) as exc: | ||
set_module_args(self.set_default_args_pass_check()) | ||
snapshot_module() | ||
exit_json(changed=True, msg="Induced arguments check") | ||
print('Info: test_module_fail_when_required_args_present: %s' % exc.value.args[0]['msg']) | ||
assert exc.value.args[0]['changed'] | ||
|
||
@patch('ansible.modules.cloud.amazon.aws_netapp_cvs_snapshots.AwsCvsNetappSnapshot.getSnapshotId') | ||
@patch('ansible.modules.cloud.amazon.aws_netapp_cvs_snapshots.AwsCvsNetappSnapshot.getfilesystemId') | ||
@patch('ansible.module_utils.netapp.AwsCvsRestAPI.post') | ||
def test_create_aws_netapp_cvs_snapshots_pass(self, get_post_api, getfilesystemId, getSnapshotId): | ||
set_module_args(self.set_args_create_aws_netapp_cvs_snapshots()) | ||
my_obj = snapshot_module() | ||
getfilesystemId.return_value = 'fiesystemName' | ||
getSnapshotId.return_value = None | ||
get_post_api.return_value = None, None | ||
with pytest.raises(AnsibleExitJson) as exc: | ||
my_obj.apply() | ||
print('Info: test_create_aws_netapp_cvs_snapshots_pass: %s' % repr(exc.value.args[0])) | ||
assert exc.value.args[0]['changed'] | ||
|
||
@patch('ansible.modules.cloud.amazon.aws_netapp_cvs_snapshots.AwsCvsNetappSnapshot.getSnapshotId') | ||
@patch('ansible.module_utils.netapp.AwsCvsRestAPI.delete') | ||
def test_delete_aws_netapp_cvs_snapshots_pass(self, get_post_api, getSnapshotId): | ||
set_module_args(self.set_args_delete_aws_netapp_cvs_snapshots()) | ||
my_obj = snapshot_module() | ||
getSnapshotId.return_value = "1f63b3d0-4fd4-b4fe-1ed6-c62f5f20d975" | ||
get_post_api.return_value = None, None | ||
with pytest.raises(AnsibleExitJson) as exc: | ||
my_obj.apply() | ||
print('Info: test_create_aws_netapp_cvs_snapshots_pass: %s' % repr(exc.value.args[0])) | ||
assert exc.value.args[0]['changed'] |