1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Remove deprecated modules and plugins. (#1347)

* Remove deprecated modules and plugins.

* Clean up BOTMETA.

* Re-add _netapp doc fragment and module utils. This is needed by the remaining na_ontap_gather_facts module scheduled for removal in 3.0.0.
This commit is contained in:
Felix Fontein 2020-11-23 14:10:18 +01:00 committed by GitHub
parent 19b5fceeab
commit 3d66ed3ae3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 58 additions and 5079 deletions

7
.github/BOTMETA.yml vendored
View file

@ -20,9 +20,6 @@ files:
maintainers: $team_macos
labels: macos say
keywords: brew cask darwin homebrew macosx macports osx
$callbacks/stderr.py:
maintainers: ysn2233
labels: stderr
$callbacks/sumologic.py:
maintainers: ryancurrah
labels: sumologic
@ -733,8 +730,6 @@ files:
maintainers: jagadeeshnv
$modules/remote_management/dellemc/ome_device_info.py:
maintainers: Sajna-Shetty
$modules/remote_management/foreman/:
maintainers: ehelms ares ekohl xprazak2
$modules/remote_management/hpilo/:
maintainers: haad
ignore: dagwieers
@ -776,8 +771,6 @@ files:
maintainers: andreparames
$modules/source_control/git_config.py:
maintainers: djmattyg007 mgedmin
$modules/source_control/github/github_hooks.py:
maintainers: pcgentry
$modules/source_control/github/github_deploy_key.py:
maintainers: bincyber
$modules/source_control/github/github_issue.py:

View file

@ -0,0 +1,20 @@
removed_features:
- The deprecated ``foreman`` module has been removed. Use the modules from the theforeman.foreman collection instead (https://github.com/ansible-collections/community.general/pull/1347) (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``katello`` module has been removed. Use the modules from the theforeman.foreman collection instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``github_hooks`` module has been removed. Use ``community.general.github_webhook`` and ``community.general.github_webhook_info`` instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_aggregate`` module has been removed. Use netapp.ontap.na_ontap_aggregate instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_license`` module has been removed. Use netapp.ontap.na_ontap_license instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_lun`` module has been removed. Use netapp.ontap.na_ontap_lun instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_qtree`` module has been removed. Use netapp.ontap.na_ontap_qtree instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_svm`` module has been removed. Use netapp.ontap.na_ontap_svm instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_user`` module has been removed. Use netapp.ontap.na_ontap_user instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_user_role`` module has been removed. Use netapp.ontap.na_ontap_user_role instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_volume`` module has been removed. Use netapp.ontap.na_ontap_volume instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``sf_account_manager`` module has been removed. Use netapp.elementsw.na_elementsw_account instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``sf_check_connections`` module has been removed. Use netapp.elementsw.na_elementsw_check_connections instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``sf_snapshot_schedule_manager`` module has been removed. Use netapp.elementsw.na_elementsw_snapshot_schedule instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``sf_volume_access_group_manager`` module has been removed. Use netapp.elementsw.na_elementsw_access_group instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``sf_volume_manager`` module has been removed. Use netapp.elementsw.na_elementsw_volume instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``actionable`` callback plugin has been removed. Use the ``ansible.builtin.default`` callback plugin with ``display_skipped_hosts = no`` and ``display_ok_hosts = no`` options instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``full_skip`` callback plugin has been removed. Use the ``ansible.builtin.default`` callback plugin with ``display_skipped_hosts = no`` option instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``stderr`` callback plugin has been removed. Use the ``ansible.builtin.default`` callback plugin with ``display_failed_stderr = yes`` option instead (https://github.com/ansible-collections/community.general/pull/1347).

View file

@ -96,9 +96,9 @@ plugin_routing:
docker_volume_info:
redirect: community.docker.docker_volume_info
foreman:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use the modules from the theforeman.foreman collection instead.
gcdns_record:
deprecation:
removal_version: 2.0.0
@ -140,9 +140,9 @@ plugin_routing:
removal_version: 2.0.0
warning_text: see plugin documentation for details
github_hooks:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use community.general.github_webhook and community.general.github_webhook_info instead.
helm:
deprecation:
removal_version: 3.0.0
@ -160,9 +160,9 @@ plugin_routing:
removal_version: 3.0.0
warning_text: see plugin documentation for details
katello:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use the modules from the theforeman.foreman collection instead.
ldap_attr:
deprecation:
removal_version: 3.0.0
@ -184,37 +184,37 @@ plugin_routing:
removal_version: 3.0.0
warning_text: see plugin documentation for details
na_cdot_aggregate:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.ontap.na_ontap_aggregate instead.
na_cdot_license:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.ontap.na_ontap_license instead.
na_cdot_lun:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.ontap.na_ontap_lun instead.
na_cdot_qtree:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.ontap.na_ontap_qtree instead.
na_cdot_svm:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.ontap.na_ontap_svm instead.
na_cdot_user:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.ontap.na_ontap_user instead.
na_cdot_user_role:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.ontap.na_ontap_user_role instead.
na_cdot_volume:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.ontap.na_ontap_volume instead.
na_ontap_gather_facts:
deprecation:
removal_version: 3.0.0
@ -416,25 +416,25 @@ plugin_routing:
removal_version: 3.0.0
warning_text: see plugin documentation for details
sf_account_manager:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.elementsw.na_elementsw_account instead.
sf_check_connections:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.elementsw.na_elementsw_check_connections instead.
sf_snapshot_schedule_manager:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.elementsw.na_elementsw_snapshot_schedule instead.
sf_volume_access_group_manager:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.elementsw.na_elementsw_access_group instead.
sf_volume_manager:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use netapp.elementsw.na_elementsw_volume instead.
smartos_image_facts:
deprecation:
removal_version: 3.0.0
@ -457,17 +457,17 @@ plugin_routing:
redirect: community.docker.swarm
callback:
actionable:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use the 'default' callback plugin with 'display_skipped_hosts = no' and 'display_ok_hosts = no' options.
full_skip:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use the 'default' callback plugin with 'display_skipped_hosts = no' option.
stderr:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use the 'default' callback plugin with 'display_failed_stderr = yes' option.
inventory:
docker_machine:
redirect: community.docker.docker_machine

View file

@ -1,61 +0,0 @@
# (c) 2015, Andrew Gaffney <andrew@agaffney.org>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
callback: actionable
type: stdout
short_description: shows only items that need attention
description:
- Use this callback when you dont care about OK nor Skipped.
- This callback suppresses any non Failed or Changed status.
deprecated:
why: The 'default' callback plugin now supports this functionality
removed_in: '2.0.0' # was Ansible 2.11
alternative: "'default' callback plugin with 'display_skipped_hosts = no' and 'display_ok_hosts = no' options"
extends_documentation_fragment:
- default_callback
requirements:
- set as stdout callback in configuration
# Override defaults from 'default' callback plugin
options:
display_skipped_hosts:
name: Show skipped hosts
description: "Toggle to control displaying skipped task/host results in a task"
type: bool
default: no
env:
- name: DISPLAY_SKIPPED_HOSTS
deprecated:
why: environment variables without "ANSIBLE_" prefix are deprecated
version: "2.0.0" # was Ansible 2.12
alternatives: the "ANSIBLE_DISPLAY_SKIPPED_HOSTS" environment variable
- name: ANSIBLE_DISPLAY_SKIPPED_HOSTS
ini:
- key: display_skipped_hosts
section: defaults
display_ok_hosts:
name: Show 'ok' hosts
description: "Toggle to control displaying 'ok' task/host results in a task"
type: bool
default: no
env:
- name: ANSIBLE_DISPLAY_OK_HOSTS
ini:
- key: display_ok_hosts
section: defaults
'''
from ansible.plugins.callback.default import CallbackModule as CallbackModule_default
class CallbackModule(CallbackModule_default):
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'stdout'
CALLBACK_NAME = 'community.general.actionable'

View file

@ -1,76 +0,0 @@
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
callback: full_skip
type: stdout
short_description: suppresses tasks if all hosts skipped
description:
- Use this plugin when you do not care about any output for tasks that were completely skipped
deprecated:
why: The 'default' callback plugin now supports this functionality
removed_in: '2.0.0' # was Ansible 2.11
alternative: "'default' callback plugin with 'display_skipped_hosts = no' option"
extends_documentation_fragment:
- default_callback
requirements:
- set as stdout in configuration
'''
from ansible.plugins.callback.default import CallbackModule as CallbackModule_default
class CallbackModule(CallbackModule_default):
'''
This is the default callback interface, which simply prints messages
to stdout when new callback events are received.
'''
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'stdout'
CALLBACK_NAME = 'community.general.full_skip'
def v2_runner_on_skipped(self, result):
self.outlines = []
def v2_playbook_item_on_skipped(self, result):
self.outlines = []
def v2_runner_item_on_skipped(self, result):
self.outlines = []
def v2_runner_on_failed(self, result, ignore_errors=False):
self.display()
super(CallbackModule, self).v2_runner_on_failed(result, ignore_errors)
def v2_playbook_on_task_start(self, task, is_conditional):
self.outlines = []
self.outlines.append("TASK [%s]" % task.get_name().strip())
if self._display.verbosity >= 2:
path = task.get_path()
if path:
self.outlines.append("task path: %s" % path)
def v2_playbook_item_on_ok(self, result):
self.display()
super(CallbackModule, self).v2_playbook_item_on_ok(result)
def v2_runner_on_ok(self, result):
self.display()
super(CallbackModule, self).v2_runner_on_ok(result)
def display(self):
if len(self.outlines) == 0:
return
(first, rest) = self.outlines[0], self.outlines[1:]
self._display.banner(first)
for line in rest:
self._display.display(line)
self.outlines = []

View file

@ -1,71 +0,0 @@
# (c) 2017, Frederic Van Espen <github@freh.be>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
callback: stderr
type: stdout
requirements:
- set as main display callback
short_description: Splits output, sending failed tasks to stderr
deprecated:
why: The 'default' callback plugin now supports this functionality
removed_in: '2.0.0' # was Ansible 2.11
alternative: "'default' callback plugin with 'display_failed_stderr = yes' option"
extends_documentation_fragment:
- default_callback
description:
- This is the stderr callback plugin, it behaves like the default callback plugin but sends error output to stderr.
- Also it does not output skipped host/task/item status
'''
from ansible import constants as C
from ansible.plugins.callback.default import CallbackModule as CallbackModule_default
class CallbackModule(CallbackModule_default):
'''
This is the stderr callback plugin, which reuses the default
callback plugin but sends error output to stderr.
'''
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'stdout'
CALLBACK_NAME = 'community.general.stderr'
def __init__(self):
self.super_ref = super(CallbackModule, self)
self.super_ref.__init__()
def v2_runner_on_failed(self, result, ignore_errors=False):
delegated_vars = result._result.get('_ansible_delegated_vars', None)
self._clean_results(result._result, result._task.action)
if self._play.strategy == 'free' and self._last_task_banner != result._task._uuid:
self._print_task_banner(result._task)
self._handle_exception(result._result, use_stderr=True)
self._handle_warnings(result._result)
if result._task.loop and 'results' in result._result:
self._process_items(result)
else:
if delegated_vars:
self._display.display("fatal: [%s -> %s]: FAILED! => %s" % (result._host.get_name(), delegated_vars['ansible_host'],
self._dump_results(result._result)), color=C.COLOR_ERROR,
stderr=True)
else:
self._display.display("fatal: [%s]: FAILED! => %s" % (result._host.get_name(), self._dump_results(result._result)),
color=C.COLOR_ERROR, stderr=True)
if ignore_errors:
self._display.display("...ignoring", color=C.COLOR_SKIP)

View file

@ -1 +0,0 @@
./remote_management/foreman/foreman.py

View file

@ -1 +0,0 @@
./source_control/github/github_hooks.py

View file

@ -1 +0,0 @@
./remote_management/foreman/katello.py

View file

@ -1 +0,0 @@
./storage/netapp/na_cdot_aggregate.py

View file

@ -1 +0,0 @@
./storage/netapp/na_cdot_license.py

View file

@ -1 +0,0 @@
./storage/netapp/na_cdot_lun.py

View file

@ -1 +0,0 @@
./storage/netapp/na_cdot_qtree.py

View file

@ -1 +0,0 @@
./storage/netapp/na_cdot_svm.py

View file

@ -1 +0,0 @@
./storage/netapp/na_cdot_user.py

View file

@ -1 +0,0 @@
./storage/netapp/na_cdot_user_role.py

View file

@ -1 +0,0 @@
./storage/netapp/na_cdot_volume.py

View file

@ -1,157 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2016, Eric D Helms <ericdhelms@gmail.com>
# 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
DOCUMENTATION = '''
---
module: foreman
short_description: Manage Foreman Resources
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: "Replaced by re-designed individual modules living at https://github.com/theforeman/foreman-ansible-modules"
alternative: https://github.com/theforeman/foreman-ansible-modules
description:
- Allows the management of Foreman resources inside your Foreman server.
author:
- Eric D Helms (@ehelms)
requirements:
- nailgun >= 0.28.0
- python >= 2.6
- datetime
options:
server_url:
description:
- URL of Foreman server.
required: true
username:
description:
- Username on Foreman server.
required: true
verify_ssl:
description:
- Whether to verify an SSL connection to Foreman server.
type: bool
default: False
password:
description:
- Password for user accessing Foreman server.
required: true
entity:
description:
- The Foreman resource that the action will be performed on (e.g. organization, host).
required: true
params:
description:
- Parameters associated to the entity resource to set or edit in dictionary format (e.g. name, description).
required: true
'''
EXAMPLES = '''
- name: Create CI Organization
community.general.foreman:
username: admin
password: admin
server_url: https://fakeserver.com
entity: organization
params:
name: My Cool New Organization
delegate_to: localhost
'''
RETURN = '''# '''
import traceback
try:
from nailgun import entities
from nailgun.config import ServerConfig
HAS_NAILGUN_PACKAGE = True
except Exception:
HAS_NAILGUN_PACKAGE = False
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
class NailGun(object):
def __init__(self, server, entities, module):
self._server = server
self._entities = entities
self._module = module
def find_organization(self, name, **params):
org = self._entities.Organization(self._server, name=name, **params)
response = org.search(set(), {'search': 'name={0}'.format(name)})
if len(response) == 1:
return response[0]
return None
def organization(self, params):
name = params['name']
del params['name']
org = self.find_organization(name, **params)
if org:
org = self._entities.Organization(self._server, name=name, id=org.id, **params)
org.update()
else:
org = self._entities.Organization(self._server, name=name, **params)
org.create()
return True
def main():
module = AnsibleModule(
argument_spec=dict(
server_url=dict(type='str', required=True),
username=dict(type='str', required=True, no_log=True),
password=dict(type='str', required=True, no_log=True),
entity=dict(type='str', required=True),
verify_ssl=dict(type='bool', default=False),
params=dict(type='dict', required=True, no_log=True),
),
supports_check_mode=True,
)
if not HAS_NAILGUN_PACKAGE:
module.fail_json(msg="Missing required nailgun module (check docs or install with: pip install nailgun")
server_url = module.params['server_url']
username = module.params['username']
password = module.params['password']
entity = module.params['entity']
params = module.params['params']
verify_ssl = module.params['verify_ssl']
server = ServerConfig(
url=server_url,
auth=(username, password),
verify=verify_ssl
)
ng = NailGun(server, entities, module)
# Lets make an connection to the server with username and password
try:
org = entities.Organization(server)
org.search()
except Exception as e:
module.fail_json(msg="Failed to connect to Foreman server: %s " % to_native(e),
exception=traceback.format_exc())
if entity == 'organization':
ng.organization(params)
module.exit_json(changed=True, result="%s updated" % entity)
else:
module.fail_json(changed=False, result="Unsupported entity supplied")
if __name__ == '__main__':
main()

View file

@ -1,615 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2016, Eric D Helms <ericdhelms@gmail.com>
# 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
DOCUMENTATION = '''
---
module: katello
short_description: Manage Katello Resources
deprecated:
removed_in: '2.0.0' # was Ansible 2.12
why: "Replaced by re-designed individual modules living at https://github.com/theforeman/foreman-ansible-modules"
alternative: https://github.com/theforeman/foreman-ansible-modules
description:
- Allows the management of Katello resources inside your Foreman server.
author:
- Eric D Helms (@ehelms)
requirements:
- nailgun >= 0.28.0
- python >= 2.6
- datetime
options:
server_url:
description:
- URL of Foreman server.
required: true
username:
description:
- Username on Foreman server.
required: true
password:
description:
- Password for user accessing Foreman server.
required: true
entity:
description:
- The Foreman resource that the action will be performed on (e.g. organization, host).
choices:
- repository
- manifest
- repository_set
- sync_plan
- content_view
- lifecycle_environment
- activation_key
- product
required: true
action:
description:
- action associated to the entity resource to set or edit in dictionary format.
- Possible Action in relation to Entitys.
- "sync (available when entity=product or entity=repository)"
- "publish (available when entity=content_view)"
- "promote (available when entity=content_view)"
choices:
- sync
- publish
- promote
required: false
params:
description:
- Parameters associated to the entity resource and action, to set or edit in dictionary format.
- Each choice may be only available with specific entitys and actions.
- "Possible Choices are in the format of param_name ([entry,action,action,...],[entity,..],...)."
- The action "None" means no action specified.
- Possible Params in relation to entity and action.
- "name ([product,sync,None], [repository,sync], [repository_set,None], [sync_plan,None],"
- "[content_view,promote,publish,None], [lifecycle_environment,None], [activation_key,None])"
- "organization ([product,sync,None] ,[repository,sync,None], [repository_set,None], [sync_plan,None], "
- "[content_view,promote,publish,None], [lifecycle_environment,None], [activation_key,None])"
- "content ([manifest,None])"
- "product ([repository,sync,None], [repository_set,None], [sync_plan,None])"
- "basearch ([repository_set,None])"
- "releaserver ([repository_set,None])"
- "sync_date ([sync_plan,None])"
- "interval ([sync_plan,None])"
- "repositories ([content_view,None])"
- "from_environment ([content_view,promote])"
- "to_environment([content_view,promote])"
- "prior ([lifecycle_environment,None])"
- "content_view ([activation_key,None])"
- "lifecycle_environment ([activation_key,None])"
required: true
task_timeout:
description:
- The timeout in seconds to wait for the started Foreman action to finish.
- If the timeout is reached and the Foreman action did not complete, the ansible task fails. However the foreman action does not get canceled.
default: 1000
required: false
verify_ssl:
description:
- verify the ssl/https connection (e.g for a valid certificate)
default: false
type: bool
required: false
'''
EXAMPLES = '''
---
# Simple Example:
- name: Create Product
community.general.katello:
username: admin
password: admin
server_url: https://fakeserver.com
entity: product
params:
name: Centos 7
delegate_to: localhost
# Abstraction Example:
# katello.yml
---
- name: "{{ name }}"
community.general.katello:
username: admin
password: admin
server_url: https://fakeserver.com
entity: "{{ entity }}"
params: "{{ params }}"
delegate_to: localhost
# tasks.yml
---
- include: katello.yml
vars:
name: Create Dev Environment
entity: lifecycle_environment
params:
name: Dev
prior: Library
organization: Default Organization
- include: katello.yml
vars:
name: Create Centos Product
entity: product
params:
name: Centos 7
organization: Default Organization
- include: katello.yml
vars:
name: Create 7.2 Repository
entity: repository
params:
name: Centos 7.2
product: Centos 7
organization: Default Organization
content_type: yum
url: http://mirror.centos.org/centos/7/os/x86_64/
- include: katello.yml
vars:
name: Create Centos 7 View
entity: content_view
params:
name: Centos 7 View
organization: Default Organization
repositories:
- name: Centos 7.2
product: Centos 7
- include: katello.yml
vars:
name: Enable RHEL Product
entity: repository_set
params:
name: Red Hat Enterprise Linux 7 Server (RPMs)
product: Red Hat Enterprise Linux Server
organization: Default Organization
basearch: x86_64
releasever: 7
- include: katello.yml
vars:
name: Promote Contentview Environment with longer timeout
task_timeout: 10800
entity: content_view
action: promote
params:
name: MyContentView
organization: MyOrganisation
from_environment: Testing
to_environment: Production
# Best Practices
# In Foreman, things can be done in parallel.
# When a conflicting action is already running,
# the task will fail instantly instead of waiting for the already running action to complete.
# So you should use a "until success" loop to catch this.
- name: Promote Contentview Environment with increased Timeout
community.general.katello:
username: ansibleuser
password: supersecret
task_timeout: 10800
entity: content_view
action: promote
params:
name: MyContentView
organization: MyOrganisation
from_environment: Testing
to_environment: Production
register: task_result
until: task_result is success
retries: 9
delay: 120
'''
RETURN = '''# '''
import datetime
import os
import traceback
try:
from nailgun import entities, entity_fields, entity_mixins
from nailgun.config import ServerConfig
HAS_NAILGUN_PACKAGE = True
except Exception:
HAS_NAILGUN_PACKAGE = False
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
class NailGun(object):
def __init__(self, server, entities, module, task_timeout):
self._server = server
self._entities = entities
self._module = module
entity_mixins.TASK_TIMEOUT = task_timeout
def find_organization(self, name, **params):
org = self._entities.Organization(self._server, name=name, **params)
response = org.search(set(), {'search': 'name={0}'.format(name)})
if len(response) == 1:
return response[0]
else:
self._module.fail_json(msg="No organization found for %s" % name)
def find_lifecycle_environment(self, name, organization):
org = self.find_organization(organization)
lifecycle_env = self._entities.LifecycleEnvironment(self._server, name=name, organization=org)
response = lifecycle_env.search()
if len(response) == 1:
return response[0]
else:
self._module.fail_json(msg="No Lifecycle Found found for %s" % name)
def find_product(self, name, organization):
org = self.find_organization(organization)
product = self._entities.Product(self._server, name=name, organization=org)
response = product.search()
if len(response) == 1:
return response[0]
else:
self._module.fail_json(msg="No Product found for %s" % name)
def find_repository(self, name, product, organization):
product = self.find_product(product, organization)
repository = self._entities.Repository(self._server, name=name, product=product)
repository._fields['organization'] = entity_fields.OneToOneField(entities.Organization)
repository.organization = product.organization
response = repository.search()
if len(response) == 1:
return response[0]
else:
self._module.fail_json(msg="No Repository found for %s" % name)
def find_content_view(self, name, organization):
org = self.find_organization(organization)
content_view = self._entities.ContentView(self._server, name=name, organization=org)
response = content_view.search()
if len(response) == 1:
return response[0]
else:
self._module.fail_json(msg="No Content View found for %s" % name)
def organization(self, params):
name = params['name']
del params['name']
org = self.find_organization(name, **params)
if org:
org = self._entities.Organization(self._server, name=name, id=org.id, **params)
org.update()
else:
org = self._entities.Organization(self._server, name=name, **params)
org.create()
return True
def manifest(self, params):
org = self.find_organization(params['organization'])
params['organization'] = org.id
try:
file = open(os.getcwd() + params['content'], 'r')
content = file.read()
finally:
file.close()
manifest = self._entities.Subscription(self._server)
try:
manifest.upload(
data={'organization_id': org.id},
files={'content': content}
)
return True
except Exception as e:
if "Import is the same as existing data" in e.message:
return False
else:
self._module.fail_json(msg="Manifest import failed with %s" % to_native(e),
exception=traceback.format_exc())
def product(self, params):
org = self.find_organization(params['organization'])
params['organization'] = org.id
product = self._entities.Product(self._server, **params)
response = product.search()
if len(response) == 1:
product.id = response[0].id
product.update()
else:
product.create()
return True
def sync_product(self, params):
org = self.find_organization(params['organization'])
product = self.find_product(params['name'], org.name)
return product.sync()
def repository(self, params):
product = self.find_product(params['product'], params['organization'])
params['product'] = product.id
del params['organization']
repository = self._entities.Repository(self._server, **params)
repository._fields['organization'] = entity_fields.OneToOneField(entities.Organization)
repository.organization = product.organization
response = repository.search()
if len(response) == 1:
repository.id = response[0].id
repository.update()
else:
repository.create()
return True
def sync_repository(self, params):
org = self.find_organization(params['organization'])
repository = self.find_repository(params['name'], params['product'], org.name)
return repository.sync()
def repository_set(self, params):
product = self.find_product(params['product'], params['organization'])
del params['product']
del params['organization']
if not product:
return False
else:
reposet = self._entities.RepositorySet(self._server, product=product, name=params['name'])
reposet = reposet.search()[0]
formatted_name = [params['name'].replace('(', '').replace(')', '')]
formatted_name.append(params['basearch'])
if 'releasever' in params:
formatted_name.append(params['releasever'])
formatted_name = ' '.join(formatted_name)
repository = self._entities.Repository(self._server, product=product, name=formatted_name)
repository._fields['organization'] = entity_fields.OneToOneField(entities.Organization)
repository.organization = product.organization
repository = repository.search()
if len(repository) == 0:
if 'releasever' in params:
reposet.enable(data={'basearch': params['basearch'], 'releasever': params['releasever']})
else:
reposet.enable(data={'basearch': params['basearch']})
return True
def sync_plan(self, params):
org = self.find_organization(params['organization'])
params['organization'] = org.id
params['sync_date'] = datetime.datetime.strptime(params['sync_date'], "%H:%M")
products = params['products']
del params['products']
sync_plan = self._entities.SyncPlan(
self._server,
name=params['name'],
organization=org
)
response = sync_plan.search()
sync_plan.sync_date = params['sync_date']
sync_plan.interval = params['interval']
if len(response) == 1:
sync_plan.id = response[0].id
sync_plan.update()
else:
response = sync_plan.create()
sync_plan.id = response[0].id
if products:
ids = []
for name in products:
product = self.find_product(name, org.name)
ids.append(product.id)
sync_plan.add_products(data={'product_ids': ids})
return True
def content_view(self, params):
org = self.find_organization(params['organization'])
content_view = self._entities.ContentView(self._server, name=params['name'], organization=org)
response = content_view.search()
if len(response) == 1:
content_view.id = response[0].id
content_view.update()
else:
content_view = content_view.create()
if params['repositories']:
repos = []
for repository in params['repositories']:
repository = self.find_repository(repository['name'], repository['product'], org.name)
repos.append(repository)
content_view.repository = repos
content_view.update(['repository'])
def find_content_view_version(self, name, organization, environment):
env = self.find_lifecycle_environment(environment, organization)
content_view = self.find_content_view(name, organization)
content_view_version = self._entities.ContentViewVersion(self._server, content_view=content_view)
response = content_view_version.search(['content_view'], {'environment_id': env.id})
if len(response) == 1:
return response[0]
else:
self._module.fail_json(msg="No Content View version found for %s" % response)
def publish(self, params):
content_view = self.find_content_view(params['name'], params['organization'])
return content_view.publish()
def promote(self, params):
to_environment = self.find_lifecycle_environment(params['to_environment'], params['organization'])
version = self.find_content_view_version(params['name'], params['organization'], params['from_environment'])
data = {'environment_id': to_environment.id}
return version.promote(data=data)
def lifecycle_environment(self, params):
org = self.find_organization(params['organization'])
prior_env = self.find_lifecycle_environment(params['prior'], params['organization'])
lifecycle_env = self._entities.LifecycleEnvironment(self._server, name=params['name'], organization=org, prior=prior_env)
response = lifecycle_env.search()
if len(response) == 1:
lifecycle_env.id = response[0].id
lifecycle_env.update()
else:
lifecycle_env.create()
return True
def activation_key(self, params):
org = self.find_organization(params['organization'])
activation_key = self._entities.ActivationKey(self._server, name=params['name'], organization=org)
response = activation_key.search()
if len(response) == 1:
activation_key.id = response[0].id
activation_key.update()
else:
activation_key.create()
if params['content_view']:
content_view = self.find_content_view(params['content_view'], params['organization'])
lifecycle_environment = self.find_lifecycle_environment(params['lifecycle_environment'], params['organization'])
activation_key.content_view = content_view
activation_key.environment = lifecycle_environment
activation_key.update()
return True
def main():
module = AnsibleModule(
argument_spec=dict(
server_url=dict(type='str', required=True),
username=dict(type='str', required=True, no_log=True),
password=dict(type='str', required=True, no_log=True),
entity=dict(type='str', required=True,
choices=['repository', 'manifest', 'repository_set', 'sync_plan',
'content_view', 'lifecycle_environment', 'activation_key', 'product']),
action=dict(type='str', choices=['sync', 'publish', 'promote']),
verify_ssl=dict(type='bool', default=False),
task_timeout=dict(type='int', default=1000),
params=dict(type='dict', required=True, no_log=True),
),
supports_check_mode=True,
)
if not HAS_NAILGUN_PACKAGE:
module.fail_json(msg="Missing required nailgun module (check docs or install with: pip install nailgun")
server_url = module.params['server_url']
username = module.params['username']
password = module.params['password']
entity = module.params['entity']
action = module.params['action']
params = module.params['params']
verify_ssl = module.params['verify_ssl']
task_timeout = module.params['task_timeout']
server = ServerConfig(
url=server_url,
auth=(username, password),
verify=verify_ssl
)
ng = NailGun(server, entities, module, task_timeout)
# Lets make an connection to the server with username and password
try:
org = entities.Organization(server)
org.search()
except Exception as e:
module.fail_json(msg="Failed to connect to Foreman server: %s " % e)
result = False
if entity == 'product':
if action == 'sync':
result = ng.sync_product(params)
else:
result = ng.product(params)
elif entity == 'repository':
if action == 'sync':
result = ng.sync_repository(params)
else:
result = ng.repository(params)
elif entity == 'manifest':
result = ng.manifest(params)
elif entity == 'repository_set':
result = ng.repository_set(params)
elif entity == 'sync_plan':
result = ng.sync_plan(params)
elif entity == 'content_view':
if action == 'publish':
result = ng.publish(params)
elif action == 'promote':
result = ng.promote(params)
else:
result = ng.content_view(params)
elif entity == 'lifecycle_environment':
result = ng.lifecycle_environment(params)
elif entity == 'activation_key':
result = ng.activation_key(params)
else:
module.fail_json(changed=False, result="Unsupported entity supplied")
module.exit_json(changed=result, result="%s updated" % entity)
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
./storage/netapp/sf_account_manager.py

View file

@ -1 +0,0 @@
./storage/netapp/sf_check_connections.py

View file

@ -1 +0,0 @@
./storage/netapp/sf_snapshot_schedule_manager.py

View file

@ -1 +0,0 @@
./storage/netapp/sf_volume_access_group_manager.py

View file

@ -1 +0,0 @@
./storage/netapp/sf_volume_manager.py

View file

@ -1,193 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Phillip Gentry <phillip@cx.com>
# 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
DOCUMENTATION = '''
---
module: github_hooks
short_description: Manages GitHub service hooks.
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Replaced by more granular modules
alternative: Use M(community.general.github_webhook) and M(community.general.github_webhook_info) instead.
description:
- Adds service hooks and removes service hooks that have an error status.
options:
user:
description:
- GitHub username.
required: true
oauthkey:
description:
- The oauth key provided by GitHub. It can be found/generated on GitHub under "Edit Your Profile" >> "Developer settings" >> "Personal Access Tokens"
required: true
repo:
description:
- >
This is the API url for the repository you want to manage hooks for. It should be in the form of: https://api.github.com/repos/user:/repo:.
Note this is different than the normal repo url.
required: true
hookurl:
description:
- When creating a new hook, this is the url that you want GitHub to post to. It is only required when creating a new hook.
required: false
action:
description:
- This tells the githooks module what you want it to do.
required: true
choices: [ "create", "cleanall", "list", "clean504" ]
validate_certs:
description:
- If C(no), SSL certificates for the target repo will not be validated. This should only be used
on personally controlled sites using self-signed certificates.
required: false
default: 'yes'
type: bool
content_type:
description:
- Content type to use for requests made to the webhook
required: false
default: 'json'
choices: ['json', 'form']
author: "Phillip Gentry, CX Inc (@pcgentry)"
'''
EXAMPLES = '''
- name: Create a new service hook ignoring duplicates
community.general.github_hooks:
action: create
hookurl: http://11.111.111.111:2222
user: '{{ gituser }}'
oauthkey: '{{ oauthkey }}'
repo: https://api.github.com/repos/pcgentry/Github-Auto-Deploy
# Cleaning all hooks for this repo that had an error on the last update.
# Since this works for all hooks in a repo it is probably best that this would be called from a handler.
- name: Clean all hooks
community.general.github_hooks:
action: cleanall
user: '{{ gituser }}'
oauthkey: '{{ oauthkey }}'
repo: '{{ repo }}'
delegate_to: localhost
'''
import json
import base64
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url
from ansible.module_utils._text import to_bytes
def request(module, url, user, oauthkey, data='', method='GET'):
auth = base64.b64encode(to_bytes('%s:%s' % (user, oauthkey)).replace('\n', ''))
headers = {
'Authorization': 'Basic %s' % auth,
}
response, info = fetch_url(module, url, headers=headers, data=data, method=method)
return response, info
def _list(module, oauthkey, repo, user):
url = "%s/hooks" % repo
response, info = request(module, url, user, oauthkey)
if info['status'] != 200:
return False, ''
else:
return False, response.read()
def _clean504(module, oauthkey, repo, user):
current_hooks = _list(module, oauthkey, repo, user)[1]
decoded = json.loads(current_hooks)
for hook in decoded:
if hook['last_response']['code'] == 504:
_delete(module, oauthkey, repo, user, hook['id'])
return 0, current_hooks
def _cleanall(module, oauthkey, repo, user):
current_hooks = _list(module, oauthkey, repo, user)[1]
decoded = json.loads(current_hooks)
for hook in decoded:
if hook['last_response']['code'] != 200:
_delete(module, oauthkey, repo, user, hook['id'])
return 0, current_hooks
def _create(module, hookurl, oauthkey, repo, user, content_type):
url = "%s/hooks" % repo
values = {
"active": True,
"name": "web",
"config": {
"url": "%s" % hookurl,
"content_type": "%s" % content_type
}
}
data = json.dumps(values)
response, info = request(module, url, user, oauthkey, data=data, method='POST')
if info['status'] != 200:
return 0, '[]'
else:
return 0, response.read()
def _delete(module, oauthkey, repo, user, hookid):
url = "%s/hooks/%s" % (repo, hookid)
response, info = request(module, url, user, oauthkey, method='DELETE')
return response.read()
def main():
module = AnsibleModule(
argument_spec=dict(
action=dict(required=True, choices=['list', 'clean504', 'cleanall', 'create']),
hookurl=dict(required=False),
oauthkey=dict(required=True, no_log=True),
repo=dict(required=True),
user=dict(required=True),
validate_certs=dict(default=True, type='bool'),
content_type=dict(default='json', choices=['json', 'form']),
)
)
action = module.params['action']
hookurl = module.params['hookurl']
oauthkey = module.params['oauthkey']
repo = module.params['repo']
user = module.params['user']
content_type = module.params['content_type']
if action == "list":
(rc, out) = _list(module, oauthkey, repo, user)
if action == "clean504":
(rc, out) = _clean504(module, oauthkey, repo, user)
if action == "cleanall":
(rc, out) = _cleanall(module, oauthkey, repo, user)
if action == "create":
(rc, out) = _create(module, hookurl, oauthkey, repo, user, content_type)
if rc != 0:
module.fail_json(msg="failed", result=out)
module.exit_json(msg="success", result=out)
if __name__ == '__main__':
main()

View file

@ -1,228 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: na_cdot_aggregate
short_description: Manage NetApp cDOT aggregates.
extends_documentation_fragment:
- community.general._netapp.ontap
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: Updated modules released with increased functionality
alternative: Use M(netapp.ontap.na_ontap_aggregate) instead.
description:
- Create or destroy aggregates on NetApp cDOT.
options:
state:
required: true
description:
- Whether the specified aggregate should exist or not.
choices: ['present', 'absent']
name:
required: true
description:
- The name of the aggregate to manage.
disk_count:
description:
- Number of disks to place into the aggregate, including parity disks.
- The disks in this newly-created aggregate come from the spare disk pool.
- The smallest disks in this pool join the aggregate first, unless the C(disk-size) argument is provided.
- Either C(disk-count) or C(disks) must be supplied. Range [0..2^31-1].
- Required when C(state=present).
'''
EXAMPLES = """
- name: Manage Aggregates
community.general.na_cdot_aggregate:
state: present
name: ansibleAggr
disk_count: 1
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Manage Aggregates
community.general.na_cdot_aggregate:
state: present
name: ansibleAggr
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppCDOTAggregate(object):
def __init__(self):
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
disk_count=dict(required=False, type='int'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['disk_count'])
],
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
self.disk_count = p['disk_count']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_ontap_zapi(module=self.module)
def get_aggr(self):
"""
Checks if aggregate exists.
:return:
True if aggregate found
False if aggregate is not found
:rtype: bool
"""
aggr_get_iter = netapp_utils.zapi.NaElement('aggr-get-iter')
query_details = netapp_utils.zapi.NaElement.create_node_with_children(
'aggr-attributes', **{'aggregate-name': self.name})
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(query_details)
aggr_get_iter.add_child_elem(query)
try:
result = self.server.invoke_successfully(aggr_get_iter,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
# Error 13040 denotes an aggregate not being found.
if to_native(e.code) == "13040":
return False
else:
self.module.fail_json(msg=to_native(e), exception=traceback.format_exc())
if (result.get_child_by_name('num-records') and
int(result.get_child_content('num-records')) >= 1):
return True
else:
return False
def create_aggr(self):
aggr_create = netapp_utils.zapi.NaElement.create_node_with_children(
'aggr-create', **{'aggregate': self.name,
'disk-count': str(self.disk_count)})
try:
self.server.invoke_successfully(aggr_create,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error provisioning aggregate %s: %s" % (self.name, to_native(e)),
exception=traceback.format_exc())
def delete_aggr(self):
aggr_destroy = netapp_utils.zapi.NaElement.create_node_with_children(
'aggr-destroy', **{'aggregate': self.name})
try:
self.server.invoke_successfully(aggr_destroy,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error removing aggregate %s: %s" % (self.name, to_native(e)),
exception=traceback.format_exc())
def rename_aggregate(self):
aggr_rename = netapp_utils.zapi.NaElement.create_node_with_children(
'aggr-rename', **{'aggregate': self.name,
'new-aggregate-name':
self.name})
try:
self.server.invoke_successfully(aggr_rename,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error renaming aggregate %s: %s" % (self.name, to_native(e)),
exception=traceback.format_exc())
def apply(self):
changed = False
aggregate_exists = self.get_aggr()
rename_aggregate = False
# check if anything needs to be changed (add/delete/update)
if aggregate_exists:
if self.state == 'absent':
changed = True
elif self.state == 'present':
if self.name is not None and not self.name == self.name:
rename_aggregate = True
changed = True
else:
if self.state == 'present':
# Aggregate does not exist, but requested state is present.
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not aggregate_exists:
self.create_aggr()
else:
if rename_aggregate:
self.rename_aggregate()
elif self.state == 'absent':
self.delete_aggr()
self.module.exit_json(changed=changed)
def main():
v = NetAppCDOTAggregate()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,296 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: na_cdot_license
short_description: Manage NetApp cDOT protocol and feature licenses
extends_documentation_fragment:
- community.general._netapp.ontap
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: Updated modules released with increased functionality
alternative: Use M(netapp.ontap.na_ontap_license) instead.
description:
- Add or remove licenses on NetApp ONTAP.
options:
remove_unused:
description:
- Remove licenses that have no controller affiliation in the cluster.
type: bool
default: false
remove_expired:
description:
- Remove licenses that have expired in the cluster.
type: bool
default: false
serial_number:
description:
- Serial number of the node associated with the license.
- This parameter is used primarily when removing license for a specific service.
- If this parameter is not provided, the cluster serial number is used by default.
licenses:
description:
- List of licenses to add or remove.
- Please note that trying to remove a non-existent license will throw an error.
suboptions:
base:
description:
- Cluster Base License
nfs:
description:
- NFS License
cifs:
description:
- CIFS License
iscsi:
description:
- iSCSI License
fcp:
description:
- FCP License
cdmi:
description:
- CDMI License
snaprestore:
description:
- SnapRestore License
snapmirror:
description:
- SnapMirror License
flexclone:
description:
- FlexClone License
snapvault:
description:
- SnapVault License
snaplock:
description:
- SnapLock License
snapmanagersuite:
description:
- SnapManagerSuite License
snapprotectapps:
description:
- SnapProtectApp License
v_storageattach:
description:
- Virtual Attached Storage License
'''
EXAMPLES = """
- name: Add licenses
community.general.na_cdot_license:
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
serial_number: #################
licenses:
nfs: #################
cifs: #################
iscsi: #################
fcp: #################
snaprestore: #################
flexclone: #################
- name: Remove licenses
community.general.na_cdot_license:
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
remove_unused: false
remove_expired: true
serial_number: #################
licenses:
nfs: remove
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppCDOTLicense(object):
def __init__(self):
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
serial_number=dict(required=False, type='str', default=None),
remove_unused=dict(default=False, type='bool'),
remove_expired=dict(default=False, type='bool'),
licenses=dict(default=False, type='dict'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
supports_check_mode=False
)
p = self.module.params
# set up state variables
self.serial_number = p['serial_number']
self.remove_unused = p['remove_unused']
self.remove_expired = p['remove_expired']
self.licenses = p['licenses']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_ontap_zapi(module=self.module)
def get_licensing_status(self):
"""
Check licensing status
:return: package (key) and licensing status (value)
:rtype: dict
"""
license_status = netapp_utils.zapi.NaElement('license-v2-status-list-info')
result = None
try:
result = self.server.invoke_successfully(license_status,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error checking license status: %s" %
to_native(e), exception=traceback.format_exc())
return_dictionary = {}
license_v2_status = result.get_child_by_name('license-v2-status')
if license_v2_status:
for license_v2_status_info in license_v2_status.get_children():
package = license_v2_status_info.get_child_content('package')
status = license_v2_status_info.get_child_content('method')
return_dictionary[package] = status
return return_dictionary
def remove_licenses(self, remove_list):
"""
Remove requested licenses
:param:
remove_list : List of packages to remove
"""
license_delete = netapp_utils.zapi.NaElement('license-v2-delete')
for package in remove_list:
license_delete.add_new_child('package', package)
if self.serial_number is not None:
license_delete.add_new_child('serial-number', self.serial_number)
try:
self.server.invoke_successfully(license_delete,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error removing license %s" %
to_native(e), exception=traceback.format_exc())
def remove_unused_licenses(self):
"""
Remove unused licenses
"""
remove_unused = netapp_utils.zapi.NaElement('license-v2-delete-unused')
try:
self.server.invoke_successfully(remove_unused,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error removing unused licenses: %s" %
to_native(e), exception=traceback.format_exc())
def remove_expired_licenses(self):
"""
Remove expired licenses
"""
remove_expired = netapp_utils.zapi.NaElement('license-v2-delete-expired')
try:
self.server.invoke_successfully(remove_expired,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error removing expired licenses: %s" %
to_native(e), exception=traceback.format_exc())
def update_licenses(self):
"""
Update licenses
"""
# Remove unused and expired licenses, if requested.
if self.remove_unused:
self.remove_unused_licenses()
if self.remove_expired:
self.remove_expired_licenses()
# Next, add/remove specific requested licenses.
license_add = netapp_utils.zapi.NaElement('license-v2-add')
codes = netapp_utils.zapi.NaElement('codes')
remove_list = []
for key, value in self.licenses.items():
str_value = str(value)
# Make sure license is not an empty string.
if str_value and str_value.strip():
if str_value.lower() == 'remove':
remove_list.append(str(key).lower())
else:
codes.add_new_child('license-code-v2', str_value)
# Remove requested licenses.
if len(remove_list) != 0:
self.remove_licenses(remove_list)
# Add requested licenses
if len(codes.get_children()) != 0:
license_add.add_child_elem(codes)
try:
self.server.invoke_successfully(license_add,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error adding licenses: %s" %
to_native(e), exception=traceback.format_exc())
def apply(self):
changed = False
# Add / Update licenses.
license_status = self.get_licensing_status()
self.update_licenses()
new_license_status = self.get_licensing_status()
if license_status != new_license_status:
changed = True
self.module.exit_json(changed=changed)
def main():
v = NetAppCDOTLicense()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,373 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: na_cdot_lun
short_description: Manage NetApp cDOT luns
extends_documentation_fragment:
- community.general._netapp.ontap
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: Updated modules released with increased functionality
alternative: Use M(netapp.ontap.na_ontap_lun) instead.
description:
- Create, destroy, resize luns on NetApp cDOT.
options:
state:
description:
- Whether the specified lun should exist or not.
required: true
choices: ['present', 'absent']
name:
description:
- The name of the lun to manage.
required: true
flexvol_name:
description:
- The name of the FlexVol the lun should exist on.
- Required when C(state=present).
size:
description:
- The size of the lun in C(size_unit).
- Required when C(state=present).
size_unit:
description:
- The unit used to interpret the size parameter.
choices: ['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb']
default: 'gb'
force_resize:
description:
- Forcibly reduce the size. This is required for reducing the size of the LUN to avoid accidentally reducing the LUN size.
default: false
force_remove:
description:
- If "true", override checks that prevent a LUN from being destroyed if it is online and mapped.
- If "false", destroying an online and mapped LUN will fail.
default: false
force_remove_fenced:
description:
- If "true", override checks that prevent a LUN from being destroyed while it is fenced.
- If "false", attempting to destroy a fenced LUN will fail.
- The default if not specified is "false". This field is available in Data ONTAP 8.2 and later.
default: false
vserver:
required: true
description:
- The name of the vserver to use.
'''
EXAMPLES = """
- name: Create LUN
community.general.na_cdot_lun:
state: present
name: ansibleLUN
flexvol_name: ansibleVolume
vserver: ansibleVServer
size: 5
size_unit: mb
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Resize Lun
community.general.na_cdot_lun:
state: present
name: ansibleLUN
force_resize: True
flexvol_name: ansibleVolume
vserver: ansibleVServer
size: 5
size_unit: gb
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppCDOTLUN(object):
def __init__(self):
self._size_unit_map = dict(
bytes=1,
b=1,
kb=1024,
mb=1024 ** 2,
gb=1024 ** 3,
tb=1024 ** 4,
pb=1024 ** 5,
eb=1024 ** 6,
zb=1024 ** 7,
yb=1024 ** 8
)
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
size=dict(type='int'),
size_unit=dict(default='gb',
choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb',
'pb', 'eb', 'zb', 'yb'], type='str'),
force_resize=dict(default=False, type='bool'),
force_remove=dict(default=False, type='bool'),
force_remove_fenced=dict(default=False, type='bool'),
flexvol_name=dict(type='str'),
vserver=dict(required=True, type='str'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['flexvol_name', 'size'])
],
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
self.size_unit = p['size_unit']
if p['size'] is not None:
self.size = p['size'] * self._size_unit_map[self.size_unit]
else:
self.size = None
self.force_resize = p['force_resize']
self.force_remove = p['force_remove']
self.force_remove_fenced = p['force_remove_fenced']
self.flexvol_name = p['flexvol_name']
self.vserver = p['vserver']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_ontap_zapi(module=self.module, vserver=self.vserver)
def get_lun(self):
"""
Return details about the LUN
:return: Details about the lun
:rtype: dict
"""
luns = []
tag = None
while True:
lun_info = netapp_utils.zapi.NaElement('lun-get-iter')
if tag:
lun_info.add_new_child('tag', tag, True)
query_details = netapp_utils.zapi.NaElement('lun-info')
query_details.add_new_child('vserver', self.vserver)
query_details.add_new_child('volume', self.flexvol_name)
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(query_details)
lun_info.add_child_elem(query)
result = self.server.invoke_successfully(lun_info, True)
if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
attr_list = result.get_child_by_name('attributes-list')
luns.extend(attr_list.get_children())
tag = result.get_child_content('next-tag')
if tag is None:
break
# The LUNs have been extracted.
# Find the specified lun and extract details.
return_value = None
for lun in luns:
path = lun.get_child_content('path')
_rest, _splitter, found_name = path.rpartition('/')
if found_name == self.name:
size = lun.get_child_content('size')
# Find out if the lun is attached
attached_to = None
lun_id = None
if lun.get_child_content('mapped') == 'true':
lun_map_list = netapp_utils.zapi.NaElement.create_node_with_children(
'lun-map-list-info', **{'path': path})
result = self.server.invoke_successfully(
lun_map_list, enable_tunneling=True)
igroups = result.get_child_by_name('initiator-groups')
if igroups:
for igroup_info in igroups.get_children():
igroup = igroup_info.get_child_content(
'initiator-group-name')
attached_to = igroup
lun_id = igroup_info.get_child_content('lun-id')
return_value = {
'name': found_name,
'size': size,
'attached_to': attached_to,
'lun_id': lun_id
}
else:
continue
return return_value
def create_lun(self):
"""
Create LUN with requested name and size
"""
path = '/vol/%s/%s' % (self.flexvol_name, self.name)
lun_create = netapp_utils.zapi.NaElement.create_node_with_children(
'lun-create-by-size', **{'path': path,
'size': str(self.size),
'ostype': 'linux'})
try:
self.server.invoke_successfully(lun_create, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error provisioning lun %s of size %s: %s" % (self.name, self.size, to_native(e)),
exception=traceback.format_exc())
def delete_lun(self):
"""
Delete requested LUN
"""
path = '/vol/%s/%s' % (self.flexvol_name, self.name)
lun_delete = netapp_utils.zapi.NaElement.create_node_with_children(
'lun-destroy', **{'path': path,
'force': str(self.force_remove),
'destroy-fenced-lun':
str(self.force_remove_fenced)})
try:
self.server.invoke_successfully(lun_delete, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error deleting lun %s: %s" % (path, to_native(e)),
exception=traceback.format_exc())
def resize_lun(self):
"""
Resize requested LUN.
:return: True if LUN was actually re-sized, false otherwise.
:rtype: bool
"""
path = '/vol/%s/%s' % (self.flexvol_name, self.name)
lun_resize = netapp_utils.zapi.NaElement.create_node_with_children(
'lun-resize', **{'path': path,
'size': str(self.size),
'force': str(self.force_resize)})
try:
self.server.invoke_successfully(lun_resize, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
if to_native(e.code) == "9042":
# Error 9042 denotes the new LUN size being the same as the
# old LUN size. This happens when there's barely any difference
# in the two sizes. For example, from 8388608 bytes to
# 8194304 bytes. This should go away if/when the default size
# requested/reported to/from the controller is changed to a
# larger unit (MB/GB/TB).
return False
else:
self.module.fail_json(msg="Error resizing lun %s: %s" % (path, to_native(e)),
exception=traceback.format_exc())
return True
def apply(self):
property_changed = False
multiple_properties_changed = False
size_changed = False
lun_exists = False
lun_detail = self.get_lun()
if lun_detail:
lun_exists = True
current_size = lun_detail['size']
if self.state == 'absent':
property_changed = True
elif self.state == 'present':
if not int(current_size) == self.size:
size_changed = True
property_changed = True
else:
if self.state == 'present':
property_changed = True
if property_changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not lun_exists:
self.create_lun()
else:
if size_changed:
# Ensure that size was actually changed. Please
# read notes in 'resize_lun' function for details.
size_changed = self.resize_lun()
if not size_changed and not \
multiple_properties_changed:
property_changed = False
elif self.state == 'absent':
self.delete_lun()
changed = property_changed or size_changed
# TODO: include other details about the lun (size, etc.)
self.module.exit_json(changed=changed)
def main():
v = NetAppCDOTLUN()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,234 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: na_cdot_qtree
short_description: Manage qtrees
extends_documentation_fragment:
- community.general._netapp.ontap
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: Updated modules released with increased functionality
alternative: Use M(netapp.ontap.na_ontap_qtree) instead.
description:
- Create or destroy Qtrees.
options:
state:
description:
- Whether the specified Qtree should exist or not.
required: true
choices: ['present', 'absent']
name:
description:
- The name of the Qtree to manage.
required: true
flexvol_name:
description:
- The name of the FlexVol the Qtree should exist on. Required when C(state=present).
vserver:
description:
- The name of the vserver to use.
required: true
'''
EXAMPLES = """
- name: Create QTree
community.general.na_cdot_qtree:
state: present
name: ansibleQTree
flexvol_name: ansibleVolume
vserver: ansibleVServer
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Rename QTree
community.general.na_cdot_qtree:
state: present
name: ansibleQTree
flexvol_name: ansibleVolume
vserver: ansibleVServer
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppCDOTQTree(object):
def __init__(self):
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
flexvol_name=dict(type='str'),
vserver=dict(required=True, type='str'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['flexvol_name'])
],
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
self.flexvol_name = p['flexvol_name']
self.vserver = p['vserver']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_ontap_zapi(module=self.module, vserver=self.vserver)
def get_qtree(self):
"""
Checks if the qtree exists.
:return:
True if qtree found
False if qtree is not found
:rtype: bool
"""
qtree_list_iter = netapp_utils.zapi.NaElement('qtree-list-iter')
query_details = netapp_utils.zapi.NaElement.create_node_with_children(
'qtree-info', **{'vserver': self.vserver,
'volume': self.flexvol_name,
'qtree': self.name})
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(query_details)
qtree_list_iter.add_child_elem(query)
result = self.server.invoke_successfully(qtree_list_iter,
enable_tunneling=True)
if (result.get_child_by_name('num-records') and
int(result.get_child_content('num-records')) >= 1):
return True
else:
return False
def create_qtree(self):
qtree_create = netapp_utils.zapi.NaElement.create_node_with_children(
'qtree-create', **{'volume': self.flexvol_name,
'qtree': self.name})
try:
self.server.invoke_successfully(qtree_create,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error provisioning qtree %s: %s" % (self.name, to_native(e)),
exception=traceback.format_exc())
def delete_qtree(self):
path = '/vol/%s/%s' % (self.flexvol_name, self.name)
qtree_delete = netapp_utils.zapi.NaElement.create_node_with_children(
'qtree-delete', **{'qtree': path})
try:
self.server.invoke_successfully(qtree_delete,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error deleting qtree %s: %s" % (path, to_native(e)),
exception=traceback.format_exc())
def rename_qtree(self):
path = '/vol/%s/%s' % (self.flexvol_name, self.name)
new_path = '/vol/%s/%s' % (self.flexvol_name, self.name)
qtree_rename = netapp_utils.zapi.NaElement.create_node_with_children(
'qtree-rename', **{'qtree': path,
'new-qtree-name': new_path})
try:
self.server.invoke_successfully(qtree_rename,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg="Error renaming qtree %s: %s" % (self.name, to_native(e)),
exception=traceback.format_exc())
def apply(self):
changed = False
qtree_exists = False
rename_qtree = False
qtree_detail = self.get_qtree()
if qtree_detail:
qtree_exists = True
if self.state == 'absent':
# Qtree exists, but requested state is 'absent'.
changed = True
elif self.state == 'present':
if self.name is not None and not self.name == \
self.name:
changed = True
rename_qtree = True
else:
if self.state == 'present':
# Qtree does not exist, but requested state is 'present'.
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not qtree_exists:
self.create_qtree()
else:
if rename_qtree:
self.rename_qtree()
elif self.state == 'absent':
self.delete_qtree()
self.module.exit_json(changed=changed)
def main():
v = NetAppCDOTQTree()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,246 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: na_cdot_svm
short_description: Manage NetApp cDOT svm
extends_documentation_fragment:
- community.general._netapp.ontap
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: Updated modules released with increased functionality
alternative: Use M(netapp.ontap.na_ontap_svm) instead.
description:
- Create or destroy svm on NetApp cDOT
options:
state:
description:
- Whether the specified SVM should exist or not.
required: true
choices: ['present', 'absent']
name:
description:
- The name of the SVM to manage.
required: true
root_volume:
description:
- Root volume of the SVM. Required when C(state=present).
root_volume_aggregate:
description:
- The aggregate on which the root volume will be created.
- Required when C(state=present).
root_volume_security_style:
description:
- Security Style of the root volume.
- When specified as part of the vserver-create, this field represents the security style for the Vserver root volume.
- When specified as part of vserver-get-iter call, this will return the list of matching Vservers.
- Possible values are 'unix', 'ntfs', 'mixed'.
- The 'unified' security style, which applies only to Infinite Volumes, cannot be applied to a Vserver's root volume.
- Valid options are "unix" for NFS, "ntfs" for CIFS, "mixed" for Mixed, "unified" for Unified.
- Required when C(state=present)
choices: ['unix', 'ntfs', 'mixed', 'unified']
'''
EXAMPLES = """
- name: Create SVM
community.general.na_cdot_svm:
state: present
name: ansibleVServer
root_volume: vol1
root_volume_aggregate: aggr1
root_volume_security_style: mixed
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppCDOTSVM(object):
def __init__(self):
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
root_volume=dict(type='str'),
root_volume_aggregate=dict(type='str'),
root_volume_security_style=dict(type='str', choices=['unix',
'ntfs',
'mixed',
'unified'
]),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['root_volume',
'root_volume_aggregate',
'root_volume_security_style'])
],
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
self.root_volume = p['root_volume']
self.root_volume_aggregate = p['root_volume_aggregate']
self.root_volume_security_style = p['root_volume_security_style']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_ontap_zapi(module=self.module)
def get_vserver(self):
"""
Checks if vserver exists.
:return:
True if vserver found
False if vserver is not found
:rtype: bool
"""
vserver_info = netapp_utils.zapi.NaElement('vserver-get-iter')
query_details = netapp_utils.zapi.NaElement.create_node_with_children(
'vserver-info', **{'vserver-name': self.name})
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(query_details)
vserver_info.add_child_elem(query)
result = self.server.invoke_successfully(vserver_info,
enable_tunneling=False)
if (result.get_child_by_name('num-records') and
int(result.get_child_content('num-records')) >= 1):
"""
TODO:
Return more relevant parameters about vserver that can
be updated by the playbook.
"""
return True
else:
return False
def create_vserver(self):
vserver_create = netapp_utils.zapi.NaElement.create_node_with_children(
'vserver-create', **{'vserver-name': self.name,
'root-volume': self.root_volume,
'root-volume-aggregate':
self.root_volume_aggregate,
'root-volume-security-style':
self.root_volume_security_style
})
try:
self.server.invoke_successfully(vserver_create,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error provisioning SVM %s with root volume %s on aggregate %s: %s'
% (self.name, self.root_volume, self.root_volume_aggregate, to_native(e)),
exception=traceback.format_exc())
def delete_vserver(self):
vserver_delete = netapp_utils.zapi.NaElement.create_node_with_children(
'vserver-destroy', **{'vserver-name': self.name})
try:
self.server.invoke_successfully(vserver_delete,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error deleting SVM %s with root volume %s on aggregate %s: %s'
% (self.name, self.root_volume, self.root_volume_aggregate, to_native(e)),
exception=traceback.format_exc())
def rename_vserver(self):
vserver_rename = netapp_utils.zapi.NaElement.create_node_with_children(
'vserver-rename', **{'vserver-name': self.name,
'new-name': self.name})
try:
self.server.invoke_successfully(vserver_rename,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error renaming SVM %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def apply(self):
changed = False
vserver_exists = self.get_vserver()
rename_vserver = False
if vserver_exists:
if self.state == 'absent':
changed = True
elif self.state == 'present':
# Update properties
pass
else:
if self.state == 'present':
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not vserver_exists:
self.create_vserver()
else:
if rename_vserver:
self.rename_vserver()
elif self.state == 'absent':
self.delete_vserver()
self.module.exit_json(changed=changed)
def main():
v = NetAppCDOTSVM()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,301 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: na_cdot_user
short_description: useradmin configuration and management
extends_documentation_fragment:
- community.general._netapp.ontap
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: Updated modules released with increased functionality
alternative: Use M(netapp.ontap.na_ontap_user) instead.
description:
- Create or destroy users.
options:
state:
description:
- Whether the specified user should exist or not.
required: true
choices: ['present', 'absent']
name:
description:
- The name of the user to manage.
required: true
application:
description:
- Applications to grant access to.
required: true
choices: ['console', 'http','ontapi','rsh','snmp','sp','ssh','telnet']
authentication_method:
description:
- Authentication method for the application.
- Not all authentication methods are valid for an application.
- Valid authentication methods for each application are as denoted in I(authentication_choices_description).
- password for console application
- password, domain, nsswitch, cert for http application.
- password, domain, nsswitch, cert for ontapi application.
- community for snmp application (when creating SNMPv1 and SNMPv2 users).
- usm and community for snmp application (when creating SNMPv3 users).
- password for sp application.
- password for rsh application.
- password for telnet application.
- password, publickey, domain, nsswitch for ssh application.
required: true
choices: ['community', 'password', 'publickey', 'domain', 'nsswitch', 'usm']
set_password:
description:
- Password for the user account.
- It is ignored for creating snmp users, but is required for creating non-snmp users.
- For an existing user, this value will be used as the new password.
role_name:
description:
- The name of the role. Required when C(state=present)
vserver:
description:
- The name of the vserver to use.
required: true
'''
EXAMPLES = """
- name: Create User
community.general.na_cdot_user:
state: present
name: SampleUser
application: ssh
authentication_method: password
set_password: apn1242183u1298u41
role_name: vsadmin
vserver: ansibleVServer
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppCDOTUser(object):
"""
Common operations to manage users and roles.
"""
def __init__(self):
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
application=dict(required=True, type='str', choices=[
'console', 'http', 'ontapi', 'rsh',
'snmp', 'sp', 'ssh', 'telnet']),
authentication_method=dict(required=True, type='str',
choices=['community', 'password',
'publickey', 'domain',
'nsswitch', 'usm']),
set_password=dict(required=False, type='str', default=None),
role_name=dict(required=False, type='str'),
vserver=dict(required=True, type='str'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['role_name'])
],
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
self.application = p['application']
self.authentication_method = p['authentication_method']
self.set_password = p['set_password']
self.role_name = p['role_name']
self.vserver = p['vserver']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_ontap_zapi(module=self.module)
def get_user(self):
"""
Checks if the user exists.
:return:
True if user found
False if user is not found
:rtype: bool
"""
security_login_get_iter = netapp_utils.zapi.NaElement('security-login-get-iter')
query_details = netapp_utils.zapi.NaElement.create_node_with_children(
'security-login-account-info', **{'vserver': self.vserver,
'user-name': self.name,
'application': self.application,
'authentication-method':
self.authentication_method})
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(query_details)
security_login_get_iter.add_child_elem(query)
try:
result = self.server.invoke_successfully(security_login_get_iter,
enable_tunneling=False)
if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
return True
else:
return False
except netapp_utils.zapi.NaApiError as e:
# Error 16034 denotes a user not being found.
if to_native(e.code) == "16034":
return False
else:
self.module.fail_json(msg='Error getting user %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def create_user(self):
user_create = netapp_utils.zapi.NaElement.create_node_with_children(
'security-login-create', **{'vserver': self.vserver,
'user-name': self.name,
'application': self.application,
'authentication-method':
self.authentication_method,
'role-name': self.role_name})
if self.set_password is not None:
user_create.add_new_child('password', self.set_password)
try:
self.server.invoke_successfully(user_create,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error creating user %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def delete_user(self):
user_delete = netapp_utils.zapi.NaElement.create_node_with_children(
'security-login-delete', **{'vserver': self.vserver,
'user-name': self.name,
'application': self.application,
'authentication-method':
self.authentication_method})
try:
self.server.invoke_successfully(user_delete,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error removing user %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def change_password(self):
"""
Changes the password
:return:
True if password updated
False if password is not updated
:rtype: bool
"""
self.server.set_vserver(self.vserver)
modify_password = netapp_utils.zapi.NaElement.create_node_with_children(
'security-login-modify-password', **{
'new-password': str(self.set_password),
'user-name': self.name})
try:
self.server.invoke_successfully(modify_password,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
if to_native(e.code) == '13114':
return False
else:
self.module.fail_json(msg='Error setting password for user %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
self.server.set_vserver(None)
return True
def apply(self):
property_changed = False
password_changed = False
user_exists = self.get_user()
if user_exists:
if self.state == 'absent':
property_changed = True
elif self.state == 'present':
if self.set_password is not None:
password_changed = self.change_password()
else:
if self.state == 'present':
# Check if anything needs to be updated
property_changed = True
if property_changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not user_exists:
self.create_user()
# Add ability to update parameters.
elif self.state == 'absent':
self.delete_user()
changed = property_changed or password_changed
self.module.exit_json(changed=changed)
def main():
v = NetAppCDOTUser()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,227 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: na_cdot_user_role
short_description: useradmin configuration and management
extends_documentation_fragment:
- community.general._netapp.ontap
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: Updated modules released with increased functionality
alternative: Use M(netapp.ontap.na_ontap_user_role) instead.
description:
- Create or destroy user roles
options:
state:
description:
- Whether the specified user should exist or not.
required: true
choices: ['present', 'absent']
name:
description:
- The name of the role to manage.
required: true
command_directory_name:
description:
- The command or command directory to which the role has an access.
required: true
access_level:
description:
- The name of the role to manage.
choices: ['none', 'readonly', 'all']
default: 'all'
vserver:
description:
- The name of the vserver to use.
required: true
'''
EXAMPLES = """
- name: Create User Role
community.general.na_cdot_user_role:
state: present
name: ansibleRole
command_directory_name: DEFAULT
access_level: none
vserver: ansibleVServer
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppCDOTUserRole(object):
def __init__(self):
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
command_directory_name=dict(required=True, type='str'),
access_level=dict(required=False, type='str', default='all',
choices=['none', 'readonly', 'all']),
vserver=dict(required=True, type='str'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
self.command_directory_name = p['command_directory_name']
self.access_level = p['access_level']
self.vserver = p['vserver']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_ontap_zapi(module=self.module)
def get_role(self):
"""
Checks if the role exists for specific command-directory-name.
:return:
True if role found
False if role is not found
:rtype: bool
"""
security_login_role_get_iter = netapp_utils.zapi.NaElement(
'security-login-role-get-iter')
query_details = netapp_utils.zapi.NaElement.create_node_with_children(
'security-login-role-info', **{'vserver': self.vserver,
'role-name': self.name,
'command-directory-name':
self.command_directory_name})
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(query_details)
security_login_role_get_iter.add_child_elem(query)
try:
result = self.server.invoke_successfully(
security_login_role_get_iter, enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
# Error 16031 denotes a role not being found.
if to_native(e.code) == "16031":
return False
else:
self.module.fail_json(msg='Error getting role %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
if (result.get_child_by_name('num-records') and
int(result.get_child_content('num-records')) >= 1):
return True
else:
return False
def create_role(self):
role_create = netapp_utils.zapi.NaElement.create_node_with_children(
'security-login-role-create', **{'vserver': self.vserver,
'role-name': self.name,
'command-directory-name':
self.command_directory_name,
'access-level':
self.access_level})
try:
self.server.invoke_successfully(role_create,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error creating role %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def delete_role(self):
role_delete = netapp_utils.zapi.NaElement.create_node_with_children(
'security-login-role-delete', **{'vserver': self.vserver,
'role-name': self.name,
'command-directory-name':
self.command_directory_name})
try:
self.server.invoke_successfully(role_delete,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error removing role %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def apply(self):
changed = False
role_exists = self.get_role()
if role_exists:
if self.state == 'absent':
changed = True
# Check if properties need to be updated
else:
if self.state == 'present':
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not role_exists:
self.create_role()
# Update properties
elif self.state == 'absent':
self.delete_role()
self.module.exit_json(changed=changed)
def main():
v = NetAppCDOTUserRole()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,437 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: na_cdot_volume
short_description: Manage NetApp cDOT volumes
extends_documentation_fragment:
- community.general._netapp.ontap
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: Updated modules released with increased functionality
alternative: Use M(netapp.ontap.na_ontap_volume) instead.
description:
- Create or destroy volumes on NetApp cDOT
options:
state:
description:
- Whether the specified volume should exist or not.
required: true
choices: ['present', 'absent']
name:
description:
- The name of the volume to manage.
required: true
infinite:
description:
- Set True if the volume is an Infinite Volume.
type: bool
default: 'no'
online:
description:
- Whether the specified volume is online, or not.
type: bool
default: 'yes'
aggregate_name:
description:
- The name of the aggregate the flexvol should exist on. Required when C(state=present).
size:
description:
- The size of the volume in (size_unit). Required when C(state=present).
size_unit:
description:
- The unit used to interpret the size parameter.
choices: ['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb']
default: 'gb'
vserver:
description:
- Name of the vserver to use.
required: true
junction_path:
description:
- Junction path where to mount the volume
required: false
export_policy:
description:
- Export policy to set for the specified junction path.
required: false
default: default
snapshot_policy:
description:
- Snapshot policy to set for the specified volume.
required: false
default: default
'''
EXAMPLES = """
- name: Create FlexVol
community.general.na_cdot_volume:
state: present
name: ansibleVolume
infinite: False
aggregate_name: aggr1
size: 20
size_unit: mb
vserver: ansibleVServer
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
junction_path: /ansibleVolume
export_policy: all_nfs_networks
snapshot_policy: daily
- name: Make FlexVol offline
community.general.na_cdot_volume:
state: present
name: ansibleVolume
infinite: False
online: False
vserver: ansibleVServer
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppCDOTVolume(object):
def __init__(self):
self._size_unit_map = dict(
bytes=1,
b=1,
kb=1024,
mb=1024 ** 2,
gb=1024 ** 3,
tb=1024 ** 4,
pb=1024 ** 5,
eb=1024 ** 6,
zb=1024 ** 7,
yb=1024 ** 8
)
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
is_infinite=dict(required=False, type='bool', default=False, aliases=['infinite']),
is_online=dict(required=False, type='bool', default=True, aliases=['online']),
size=dict(type='int'),
size_unit=dict(default='gb',
choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb',
'pb', 'eb', 'zb', 'yb'], type='str'),
aggregate_name=dict(type='str'),
vserver=dict(required=True, type='str', default=None),
junction_path=dict(required=False, type='str', default=None),
export_policy=dict(required=False, type='str', default='default'),
snapshot_policy=dict(required=False, type='str', default='default'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['aggregate_name', 'size'])
],
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
self.is_infinite = p['is_infinite']
self.is_online = p['is_online']
self.size_unit = p['size_unit']
self.vserver = p['vserver']
self.junction_path = p['junction_path']
self.export_policy = p['export_policy']
self.snapshot_policy = p['snapshot_policy']
if p['size'] is not None:
self.size = p['size'] * self._size_unit_map[self.size_unit]
else:
self.size = None
self.aggregate_name = p['aggregate_name']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_ontap_zapi(module=self.module, vserver=self.vserver)
def get_volume(self):
"""
Return details about the volume
:param:
name : Name of the volume
:return: Details about the volume. None if not found.
:rtype: dict
"""
volume_info = netapp_utils.zapi.NaElement('volume-get-iter')
volume_attributes = netapp_utils.zapi.NaElement('volume-attributes')
volume_id_attributes = netapp_utils.zapi.NaElement('volume-id-attributes')
volume_id_attributes.add_new_child('name', self.name)
volume_attributes.add_child_elem(volume_id_attributes)
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(volume_attributes)
volume_info.add_child_elem(query)
result = self.server.invoke_successfully(volume_info, True)
return_value = None
if result.get_child_by_name('num-records') and \
int(result.get_child_content('num-records')) >= 1:
volume_attributes = result.get_child_by_name(
'attributes-list').get_child_by_name(
'volume-attributes')
# Get volume's current size
volume_space_attributes = volume_attributes.get_child_by_name(
'volume-space-attributes')
current_size = volume_space_attributes.get_child_content('size')
# Get volume's state (online/offline)
volume_state_attributes = volume_attributes.get_child_by_name(
'volume-state-attributes')
current_state = volume_state_attributes.get_child_content('state')
is_online = None
if current_state == "online":
is_online = True
elif current_state == "offline":
is_online = False
return_value = {
'name': self.name,
'size': current_size,
'is_online': is_online,
}
return return_value
def create_volume(self):
create_parameters = {'volume': self.name,
'containing-aggr-name': self.aggregate_name,
'size': str(self.size),
}
if self.junction_path:
create_parameters['junction-path'] = str(self.junction_path)
if self.export_policy != 'default':
create_parameters['export-policy'] = str(self.export_policy)
if self.snapshot_policy != 'default':
create_parameters['snapshot-policy'] = str(self.snapshot_policy)
volume_create = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-create', **create_parameters)
try:
self.server.invoke_successfully(volume_create,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error provisioning volume %s of size %s: %s' % (self.name, self.size, to_native(e)),
exception=traceback.format_exc())
def delete_volume(self):
if self.is_infinite:
volume_delete = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-destroy-async', **{'volume-name': self.name})
else:
volume_delete = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-destroy', **{'name': self.name, 'unmount-and-offline':
'true'})
try:
self.server.invoke_successfully(volume_delete,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error deleting volume %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def rename_volume(self):
"""
Rename the volume.
Note: 'is_infinite' needs to be set to True in order to rename an
Infinite Volume.
"""
if self.is_infinite:
volume_rename = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-rename-async',
**{'volume-name': self.name, 'new-volume-name': str(
self.name)})
else:
volume_rename = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-rename', **{'volume': self.name, 'new-volume-name': str(
self.name)})
try:
self.server.invoke_successfully(volume_rename,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error renaming volume %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def resize_volume(self):
"""
Re-size the volume.
Note: 'is_infinite' needs to be set to True in order to rename an
Infinite Volume.
"""
if self.is_infinite:
volume_resize = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-size-async',
**{'volume-name': self.name, 'new-size': str(
self.size)})
else:
volume_resize = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-size', **{'volume': self.name, 'new-size': str(
self.size)})
try:
self.server.invoke_successfully(volume_resize,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error re-sizing volume %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def change_volume_state(self):
"""
Change volume's state (offline/online).
Note: 'is_infinite' needs to be set to True in order to change the
state of an Infinite Volume.
"""
state_requested = None
if self.is_online:
# Requested state is 'online'.
state_requested = "online"
if self.is_infinite:
volume_change_state = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-online-async',
**{'volume-name': self.name})
else:
volume_change_state = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-online',
**{'name': self.name})
else:
# Requested state is 'offline'.
state_requested = "offline"
if self.is_infinite:
volume_change_state = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-offline-async',
**{'volume-name': self.name})
else:
volume_change_state = netapp_utils.zapi.NaElement.create_node_with_children(
'volume-offline',
**{'name': self.name})
try:
self.server.invoke_successfully(volume_change_state,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error changing the state of volume %s to %s: %s' %
(self.name, state_requested, to_native(e)),
exception=traceback.format_exc())
def apply(self):
changed = False
volume_exists = False
rename_volume = False
resize_volume = False
volume_detail = self.get_volume()
if volume_detail:
volume_exists = True
if self.state == 'absent':
changed = True
elif self.state == 'present':
if str(volume_detail['size']) != str(self.size):
resize_volume = True
changed = True
if (volume_detail['is_online'] is not None) and (volume_detail['is_online'] != self.is_online):
changed = True
if self.is_online is False:
# Volume is online, but requested state is offline
pass
else:
# Volume is offline but requested state is online
pass
else:
if self.state == 'present':
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not volume_exists:
self.create_volume()
else:
if resize_volume:
self.resize_volume()
if volume_detail['is_online'] is not \
None and volume_detail['is_online'] != \
self.is_online:
self.change_volume_state()
# Ensure re-naming is the last change made.
if rename_volume:
self.rename_volume()
elif self.state == 'absent':
self.delete_volume()
self.module.exit_json(changed=changed)
def main():
v = NetAppCDOTVolume()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,263 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: sf_account_manager
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: This Module has been replaced
alternative: please use M(netapp.elementsw.na_elementsw_account)
short_description: Manage SolidFire accounts
extends_documentation_fragment:
- community.general._netapp.solidfire
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
description:
- Create, destroy, or update accounts on SolidFire
options:
state:
description:
- Whether the specified account should exist or not.
required: true
choices: ['present', 'absent']
name:
description:
- Unique username for this account. (May be 1 to 64 characters in length).
required: true
new_name:
description:
- New name for the user account.
initiator_secret:
description:
- CHAP secret to use for the initiator. Should be 12-16 characters long and impenetrable.
- The CHAP initiator secrets must be unique and cannot be the same as the target CHAP secret.
- If not specified, a random secret is created.
target_secret:
description:
- CHAP secret to use for the target (mutual CHAP authentication).
- Should be 12-16 characters long and impenetrable.
- The CHAP target secrets must be unique and cannot be the same as the initiator CHAP secret.
- If not specified, a random secret is created.
attributes:
description: List of Name/Value pairs in JSON object format.
account_id:
description:
- The ID of the account to manage or update.
status:
description:
- Status of the account.
'''
EXAMPLES = """
- name: Create Account
community.general.sf_account_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: present
name: TenantA
- name: Modify Account
community.general.sf_account_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: present
name: TenantA
new_name: TenantA-Renamed
- name: Delete Account
community.general.sf_account_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: absent
name: TenantA-Renamed
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_SF_SDK = netapp_utils.has_sf_sdk()
class SolidFireAccount(object):
def __init__(self):
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
account_id=dict(required=False, type='int', default=None),
new_name=dict(required=False, type='str', default=None),
initiator_secret=dict(required=False, type='str'),
target_secret=dict(required=False, type='str'),
attributes=dict(required=False, type='dict'),
status=dict(required=False, type='str'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
self.account_id = p['account_id']
self.new_name = p['new_name']
self.initiator_secret = p['initiator_secret']
self.target_secret = p['target_secret']
self.attributes = p['attributes']
self.status = p['status']
if HAS_SF_SDK is False:
self.module.fail_json(msg="Unable to import the SolidFire Python SDK")
else:
self.sfe = netapp_utils.create_sf_connection(module=self.module)
def get_account(self):
"""
Return account object if found
:return: Details about the account. None if not found.
:rtype: dict
"""
account_list = self.sfe.list_accounts()
for account in account_list.accounts:
if account.username == self.name:
# Update self.account_id:
if self.account_id is not None:
if account.account_id == self.account_id:
return account
else:
self.account_id = account.account_id
return account
return None
def create_account(self):
try:
self.sfe.add_account(username=self.name,
initiator_secret=self.initiator_secret,
target_secret=self.target_secret,
attributes=self.attributes)
except Exception as e:
self.module.fail_json(msg='Error creating account %s: %s)' % (self.name, to_native(e)),
exception=traceback.format_exc())
def delete_account(self):
try:
self.sfe.remove_account(account_id=self.account_id)
except Exception as e:
self.module.fail_json(msg='Error deleting account %s: %s' % (self.account_id, to_native(e)),
exception=traceback.format_exc())
def update_account(self):
try:
self.sfe.modify_account(account_id=self.account_id,
username=self.new_name,
status=self.status,
initiator_secret=self.initiator_secret,
target_secret=self.target_secret,
attributes=self.attributes)
except Exception as e:
self.module.fail_json(msg='Error updating account %s: %s' % (self.account_id, to_native(e)),
exception=traceback.format_exc())
def apply(self):
changed = False
account_exists = False
update_account = False
account_detail = self.get_account()
if account_detail:
account_exists = True
if self.state == 'absent':
changed = True
elif self.state == 'present':
# Check if we need to update the account
if account_detail.username is not None and self.new_name is not None and \
account_detail.username != self.new_name:
update_account = True
changed = True
elif account_detail.status is not None and self.status is not None \
and account_detail.status != self.status:
update_account = True
changed = True
elif account_detail.initiator_secret is not None and self.initiator_secret is not None \
and account_detail.initiator_secret != self.initiator_secret:
update_account = True
changed = True
elif account_detail.target_secret is not None and self.target_secret is not None \
and account_detail.target_secret != self.target_secret:
update_account = True
changed = True
elif account_detail.attributes is not None and self.attributes is not None \
and account_detail.attributes != self.attributes:
update_account = True
changed = True
else:
if self.state == 'present':
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not account_exists:
self.create_account()
elif update_account:
self.update_account()
elif self.state == 'absent':
self.delete_account()
self.module.exit_json(changed=changed)
def main():
v = SolidFireAccount()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,179 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: sf_check_connections
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: This Module has been replaced
alternative: please use M(netapp.elementsw.na_elementsw_check_connections)
short_description: Check connectivity to MVIP and SVIP.
extends_documentation_fragment:
- community.general._netapp.solidfire
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
description:
- Used to test the management connection to the cluster.
- The test pings the MVIP and SVIP, and executes a simple API method to verify connectivity.
options:
skip:
description:
- Skip checking connection to SVIP or MVIP.
choices: ['svip', 'mvip']
mvip:
description:
- Optionally, use to test connection of a different MVIP.
- This is not needed to test the connection to the target cluster.
svip:
description:
- Optionally, use to test connection of a different SVIP.
- This is not needed to test the connection to the target cluster.
'''
EXAMPLES = """
- name: Check connections to MVIP and SVIP
community.general.sf_check_connections:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_SF_SDK = netapp_utils.has_sf_sdk()
class SolidFireConnection(object):
def __init__(self):
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
skip=dict(required=False, type='str', default=None, choices=['mvip', 'svip']),
mvip=dict(required=False, type='str', default=None),
svip=dict(required=False, type='str', default=None)
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.skip = p['skip']
self.mvip = p['mvip']
self.svip = p['svip']
if HAS_SF_SDK is False:
self.module.fail_json(msg="Unable to import the SolidFire Python SDK")
else:
self.sfe = netapp_utils.ElementFactory.create(p['hostname'], p['username'], p['password'], port=442)
def check_mvip_connection(self):
"""
Check connection to MVIP
:return: true if connection was successful, false otherwise.
:rtype: bool
"""
try:
test = self.sfe.test_connect_mvip(mvip=self.mvip)
result = test.details.connected
# Todo - Log details about the test
return result
except Exception as e:
self.module.fail_json(msg='Error checking connection to MVIP: %s' % to_native(e), exception=traceback.format_exc())
return False
def check_svip_connection(self):
"""
Check connection to SVIP
:return: true if connection was successful, false otherwise.
:rtype: bool
"""
try:
test = self.sfe.test_connect_svip(svip=self.svip)
result = test.details.connected
# Todo - Log details about the test
return result
except Exception as e:
self.module.fail_json(msg='Error checking connection to SVIP: %s' % to_native(e), exception=traceback.format_exc())
return False
def check(self):
failed = True
msg = ''
if self.skip is None:
mvip_connection_established = self.check_mvip_connection()
svip_connection_established = self.check_svip_connection()
# Set failed and msg
if not mvip_connection_established:
failed = True
msg = 'Connection to MVIP failed.'
elif not svip_connection_established:
failed = True
msg = 'Connection to SVIP failed.'
else:
failed = False
elif self.skip == 'mvip':
svip_connection_established = self.check_svip_connection()
# Set failed and msg
if not svip_connection_established:
failed = True
msg = 'Connection to SVIP failed.'
else:
failed = False
elif self.skip == 'svip':
mvip_connection_established = self.check_mvip_connection()
# Set failed and msg
if not mvip_connection_established:
failed = True
msg = 'Connection to MVIP failed.'
else:
failed = False
if failed:
self.module.fail_json(msg=msg)
else:
self.module.exit_json()
def main():
v = SolidFireConnection()
v.check()
if __name__ == '__main__':
main()

View file

@ -1,384 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: sf_snapshot_schedule_manager
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: This Module has been replaced
alternative: please use M(netapp.elementsw.na_elementsw_snapshot_schedule)
short_description: Manage SolidFire snapshot schedules
extends_documentation_fragment:
- community.general._netapp.solidfire
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
description:
- Create, destroy, or update accounts on SolidFire
options:
state:
description:
- Whether the specified schedule should exist or not.
required: true
choices: ['present', 'absent']
paused:
description:
- Pause / Resume a schedule.
required: false
recurring:
description:
- Should the schedule recur?
required: false
time_interval_days:
description: Time interval in days.
required: false
default: 1
time_interval_hours:
description: Time interval in hours.
required: false
default: 0
time_interval_minutes:
description: Time interval in minutes.
required: false
default: 0
name:
description:
- Name for the snapshot schedule.
required: true
snapshot_name:
description:
- Name for the created snapshots.
required: false
volumes:
description:
- Volume IDs that you want to set the snapshot schedule for.
- At least 1 volume ID is required for creating a new schedule.
- required when C(state=present)
required: false
retention:
description:
- Retention period for the snapshot.
- Format is 'HH:mm:ss'.
required: false
schedule_id:
description:
- The schedule ID for the schedule that you want to update or delete.
required: false
starting_date:
description:
- Starting date for the schedule.
- Required when C(state=present).
- Please use two '-' in the above format, or you may see an error- TypeError, is not JSON serializable description.
- "Format: C(2016--12--01T00:00:00Z)"
required: false
'''
EXAMPLES = """
- name: Create Snapshot schedule
community.general.sf_snapshot_schedule_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: present
name: Schedule_A
time_interval_days: 1
starting_date: 2016--12--01T00:00:00Z
volumes: 7
- name: Update Snapshot schedule
community.general.sf_snapshot_schedule_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: present
schedule_id: 6
recurring: True
snapshot_name: AnsibleSnapshots
- name: Delete Snapshot schedule
community.general.sf_snapshot_schedule_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: absent
schedule_id: 6
"""
RETURN = """
schedule_id:
description: Schedule ID of the newly created schedule
returned: success
type: str
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_SF_SDK = netapp_utils.has_sf_sdk()
class SolidFireSnapShotSchedule(object):
def __init__(self):
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
time_interval_days=dict(required=False, type='int', default=1),
time_interval_hours=dict(required=False, type='int', default=0),
time_interval_minutes=dict(required=False, type='int', default=0),
paused=dict(required=False, type='bool'),
recurring=dict(required=False, type='bool'),
starting_date=dict(type='str'),
snapshot_name=dict(required=False, type='str'),
volumes=dict(required=False, type='list'),
retention=dict(required=False, type='str'),
schedule_id=dict(type='int'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['starting_date', 'volumes'])
],
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
# self.interval = p['interval']
self.time_interval_days = p['time_interval_days']
self.time_interval_hours = p['time_interval_hours']
self.time_interval_minutes = p['time_interval_minutes']
self.paused = p['paused']
self.recurring = p['recurring']
self.starting_date = p['starting_date']
if self.starting_date is not None:
self.starting_date = self.starting_date.replace("--", "-")
self.snapshot_name = p['snapshot_name']
self.volumes = p['volumes']
self.retention = p['retention']
self.schedule_id = p['schedule_id']
self.create_schedule_result = None
if HAS_SF_SDK is False:
self.module.fail_json(msg="Unable to import the SolidFire Python SDK")
else:
self.sfe = netapp_utils.create_sf_connection(module=self.module)
def get_schedule(self):
schedule_list = self.sfe.list_schedules()
for schedule in schedule_list.schedules:
if schedule.name == self.name:
# Update self.schedule_id:
if self.schedule_id is not None:
if schedule.schedule_id == self.schedule_id:
return schedule
else:
self.schedule_id = schedule.schedule_id
return schedule
return None
def create_schedule(self):
try:
sched = netapp_utils.Schedule()
# if self.interval == 'time_interval':
sched.frequency = netapp_utils.TimeIntervalFrequency(days=self.time_interval_days,
hours=self.time_interval_hours,
minutes=self.time_interval_minutes)
# Create schedule
sched.name = self.name
sched.schedule_info = netapp_utils.ScheduleInfo(
volume_ids=self.volumes,
snapshot_name=self.snapshot_name,
retention=self.retention
)
sched.paused = self.paused
sched.recurring = self.recurring
sched.starting_date = self.starting_date
self.create_schedule_result = self.sfe.create_schedule(schedule=sched)
except Exception as e:
self.module.fail_json(msg='Error creating schedule %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def delete_schedule(self):
try:
get_schedule_result = self.sfe.get_schedule(schedule_id=self.schedule_id)
sched = get_schedule_result.schedule
sched.to_be_deleted = True
self.sfe.modify_schedule(schedule=sched)
except Exception as e:
self.module.fail_json(msg='Error deleting schedule %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def update_schedule(self):
try:
get_schedule_result = self.sfe.get_schedule(schedule_id=self.schedule_id)
sched = get_schedule_result.schedule
# Update schedule properties
# if self.interval == 'time_interval':
temp_frequency = netapp_utils.TimeIntervalFrequency(days=self.time_interval_days,
hours=self.time_interval_hours,
minutes=self.time_interval_minutes)
if sched.frequency.days != temp_frequency.days or \
sched.frequency.hours != temp_frequency.hours \
or sched.frequency.minutes != temp_frequency.minutes:
sched.frequency = temp_frequency
sched.name = self.name
if self.volumes is not None:
sched.schedule_info.volume_ids = self.volumes
if self.retention is not None:
sched.schedule_info.retention = self.retention
if self.snapshot_name is not None:
sched.schedule_info.snapshot_name = self.snapshot_name
if self.paused is not None:
sched.paused = self.paused
if self.recurring is not None:
sched.recurring = self.recurring
if self.starting_date is not None:
sched.starting_date = self.starting_date
# Make API call
self.sfe.modify_schedule(schedule=sched)
except Exception as e:
self.module.fail_json(msg='Error updating schedule %s: %s' % (self.name, to_native(e)),
exception=traceback.format_exc())
def apply(self):
changed = False
schedule_exists = False
update_schedule = False
schedule_detail = self.get_schedule()
if schedule_detail:
schedule_exists = True
if self.state == 'absent':
changed = True
elif self.state == 'present':
# Check if we need to update the account
if self.retention is not None and schedule_detail.schedule_info.retention != self.retention:
update_schedule = True
changed = True
elif schedule_detail.name != self.name:
update_schedule = True
changed = True
elif self.snapshot_name is not None and schedule_detail.schedule_info.snapshot_name != self.snapshot_name:
update_schedule = True
changed = True
elif self.volumes is not None and schedule_detail.schedule_info.volume_ids != self.volumes:
update_schedule = True
changed = True
elif self.paused is not None and schedule_detail.paused != self.paused:
update_schedule = True
changed = True
elif self.recurring is not None and schedule_detail.recurring != self.recurring:
update_schedule = True
changed = True
elif self.starting_date is not None and schedule_detail.starting_date != self.starting_date:
update_schedule = True
changed = True
elif self.time_interval_minutes is not None or self.time_interval_hours is not None \
or self.time_interval_days is not None:
temp_frequency = netapp_utils.TimeIntervalFrequency(days=self.time_interval_days,
hours=self.time_interval_hours,
minutes=self.time_interval_minutes)
if schedule_detail.frequency.days != temp_frequency.days or \
schedule_detail.frequency.hours != temp_frequency.hours \
or schedule_detail.frequency.minutes != temp_frequency.minutes:
update_schedule = True
changed = True
else:
if self.state == 'present':
changed = True
if changed:
if self.module.check_mode:
# Skip changes
pass
else:
if self.state == 'present':
if not schedule_exists:
self.create_schedule()
elif update_schedule:
self.update_schedule()
elif self.state == 'absent':
self.delete_schedule()
if self.create_schedule_result is not None:
self.module.exit_json(changed=changed, schedule_id=self.create_schedule_result.schedule_id)
else:
self.module.exit_json(changed=changed)
def main():
v = SolidFireSnapShotSchedule()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,244 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: sf_volume_access_group_manager
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: This Module has been replaced
alternative: please use M(netapp.elementsw.na_elementsw_access_group)
short_description: Manage SolidFire Volume Access Groups
extends_documentation_fragment:
- community.general._netapp.solidfire
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
description:
- Create, destroy, or update volume access groups on SolidFire
options:
state:
description:
- Whether the specified volume access group should exist or not.
required: true
choices: ['present', 'absent']
name:
description:
- Name of the volume access group. It is not required to be unique, but recommended.
required: true
initiators:
description:
- List of initiators to include in the volume access group. If unspecified, the access group will start out without configured initiators.
volumes:
description:
- List of volumes to initially include in the volume access group. If unspecified, the access group will start without any volumes.
virtual_network_id:
description:
- The ID of the SolidFire Virtual Network ID to associate the volume access group with.
virtual_network_tags:
description:
- The ID of the VLAN Virtual Network Tag to associate the volume access group with.
attributes:
description: List of Name/Value pairs in JSON object format.
volume_access_group_id:
description:
- The ID of the volume access group to modify or delete.
'''
EXAMPLES = """
- name: Create Volume Access Group
community.general.sf_volume_access_group_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: present
name: AnsibleVolumeAccessGroup
volumes: [7,8]
- name: Modify Volume Access Group
community.general.sf_volume_access_group_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: present
volume_access_group_id: 1
name: AnsibleVolumeAccessGroup-Renamed
attributes: {"volumes": [1,2,3], "virtual_network_id": 12345}
- name: Delete Volume Access Group
community.general.sf_volume_access_group_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: absent
volume_access_group_id: 1
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_SF_SDK = netapp_utils.has_sf_sdk()
class SolidFireVolumeAccessGroup(object):
def __init__(self):
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
volume_access_group_id=dict(required=False, type='int', default=None),
initiators=dict(required=False, type='list', default=None),
volumes=dict(required=False, type='list', default=None),
virtual_network_id=dict(required=False, type='list', default=None),
virtual_network_tags=dict(required=False, type='list', default=None),
attributes=dict(required=False, type='dict', default=None),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
self.volume_access_group_id = p['volume_access_group_id']
self.initiators = p['initiators']
self.volumes = p['volumes']
self.virtual_network_id = p['virtual_network_id']
self.virtual_network_tags = p['virtual_network_tags']
self.attributes = p['attributes']
if HAS_SF_SDK is False:
self.module.fail_json(msg="Unable to import the SolidFire Python SDK")
else:
self.sfe = netapp_utils.create_sf_connection(module=self.module)
def get_volume_access_group(self):
access_groups_list = self.sfe.list_volume_access_groups()
for group in access_groups_list.volume_access_groups:
if group.name == self.name:
# Update self.volume_access_group_id:
if self.volume_access_group_id is not None:
if group.volume_access_group_id == self.volume_access_group_id:
return group
else:
self.volume_access_group_id = group.volume_access_group_id
return group
return None
def create_volume_access_group(self):
try:
self.sfe.create_volume_access_group(name=self.name,
initiators=self.initiators,
volumes=self.volumes,
virtual_network_id=self.virtual_network_id,
virtual_network_tags=self.virtual_network_tags,
attributes=self.attributes)
except Exception as e:
self.module.fail_json(msg="Error creating volume access group %s: %s" %
(self.name, to_native(e)), exception=traceback.format_exc())
def delete_volume_access_group(self):
try:
self.sfe.delete_volume_access_group(volume_access_group_id=self.volume_access_group_id)
except Exception as e:
self.module.fail_json(msg="Error deleting volume access group %s: %s" %
(self.volume_access_group_id, to_native(e)),
exception=traceback.format_exc())
def update_volume_access_group(self):
try:
self.sfe.modify_volume_access_group(volume_access_group_id=self.volume_access_group_id,
virtual_network_id=self.virtual_network_id,
virtual_network_tags=self.virtual_network_tags,
name=self.name,
initiators=self.initiators,
volumes=self.volumes,
attributes=self.attributes)
except Exception as e:
self.module.fail_json(msg="Error updating volume access group %s: %s" %
(self.volume_access_group_id, to_native(e)), exception=traceback.format_exc())
def apply(self):
changed = False
group_exists = False
update_group = False
group_detail = self.get_volume_access_group()
if group_detail:
group_exists = True
if self.state == 'absent':
changed = True
elif self.state == 'present':
# Check if we need to update the group
if self.volumes is not None and group_detail.volumes != self.volumes:
update_group = True
changed = True
elif self.initiators is not None and group_detail.initiators != self.initiators:
update_group = True
changed = True
elif self.virtual_network_id is not None or self.virtual_network_tags is not None or \
self.attributes is not None:
update_group = True
changed = True
else:
if self.state == 'present':
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not group_exists:
self.create_volume_access_group()
elif update_group:
self.update_volume_access_group()
elif self.state == 'absent':
self.delete_volume_access_group()
self.module.exit_json(changed=changed)
def main():
v = SolidFireVolumeAccessGroup()
v.apply()
if __name__ == '__main__':
main()

View file

@ -1,315 +0,0 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# 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
DOCUMENTATION = '''
module: sf_volume_manager
deprecated:
removed_in: 2.0.0 # was Ansible 2.11
why: This Module has been replaced
alternative: please use M(netapp.elementsw.na_elementsw_volume)
short_description: Manage SolidFire volumes
extends_documentation_fragment:
- community.general._netapp.solidfire
author: Sumit Kumar (@timuster) <sumit4@netapp.com>
description:
- Create, destroy, or update volumes on SolidFire
options:
state:
description:
- Whether the specified volume should exist or not.
required: true
choices: ['present', 'absent']
name:
description:
- The name of the volume to manage.
required: true
account_id:
description:
- Account ID for the owner of this volume.
required: true
512emulation:
description:
- Should the volume provide 512-byte sector emulation?
- Required when C(state=present)
qos:
description: Initial quality of service settings for this volume. Configure as dict in playbooks.
attributes:
description: A YAML dictionary of attributes that you would like to apply on this volume.
volume_id:
description:
- The ID of the volume to manage or update.
- In order to create multiple volumes with the same name, but different volume_ids, please declare the I(volume_id)
parameter with an arbitrary value. However, the specified volume_id will not be assigned to the newly created
volume (since it's an auto-generated property).
size:
description:
- The size of the volume in (size_unit).
- Required when C(state = present).
size_unit:
description:
- The unit used to interpret the size parameter.
choices: ['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb']
default: 'gb'
access:
description:
- "Access allowed for the volume."
- "readOnly: Only read operations are allowed."
- "readWrite: Reads and writes are allowed."
- "locked: No reads or writes are allowed."
- "replicationTarget: Identify a volume as the target volume for a paired set of volumes. If the volume is not paired, the access status is locked."
- "If unspecified, the access settings of the clone will be the same as the source."
choices: ['readOnly', 'readWrite', 'locked', 'replicationTarget']
'''
EXAMPLES = """
- name: Create Volume
community.general.sf_volume_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: present
name: AnsibleVol
qos: {minIOPS: 1000, maxIOPS: 20000, burstIOPS: 50000}
account_id: 3
enable512e: False
size: 1
size_unit: gb
- name: Update Volume
community.general.sf_volume_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: present
name: AnsibleVol
account_id: 3
access: readWrite
- name: Delete Volume
community.general.sf_volume_manager:
hostname: "{{ solidfire_hostname }}"
username: "{{ solidfire_username }}"
password: "{{ solidfire_password }}"
state: absent
name: AnsibleVol
account_id: 2
"""
RETURN = """
msg:
description: Success message
returned: success
type: str
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.community.general.plugins.module_utils._netapp as netapp_utils
HAS_SF_SDK = netapp_utils.has_sf_sdk()
class SolidFireVolume(object):
def __init__(self):
self._size_unit_map = netapp_utils.SF_BYTE_MAP
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
account_id=dict(required=True, type='int'),
enable512e=dict(type='bool', aliases=['512emulation']),
qos=dict(required=False, type='dict', default=None),
attributes=dict(required=False, type='dict', default=None),
volume_id=dict(type='int', default=None),
size=dict(type='int'),
size_unit=dict(default='gb',
choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb',
'pb', 'eb', 'zb', 'yb'], type='str'),
access=dict(required=False, type='str', default=None, choices=['readOnly', 'readWrite',
'locked', 'replicationTarget']),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['size', 'enable512e'])
],
supports_check_mode=True
)
p = self.module.params
# set up state variables
self.state = p['state']
self.name = p['name']
self.account_id = p['account_id']
self.enable512e = p['enable512e']
self.qos = p['qos']
self.attributes = p['attributes']
self.volume_id = p['volume_id']
self.size_unit = p['size_unit']
if p['size'] is not None:
self.size = p['size'] * self._size_unit_map[self.size_unit]
else:
self.size = None
self.access = p['access']
if HAS_SF_SDK is False:
self.module.fail_json(msg="Unable to import the SolidFire Python SDK")
else:
self.sfe = netapp_utils.create_sf_connection(module=self.module)
def get_volume(self):
"""
Return volume object if found
:return: Details about the volume. None if not found.
:rtype: dict
"""
volume_list = self.sfe.list_volumes_for_account(account_id=self.account_id)
for volume in volume_list.volumes:
if volume.name == self.name:
# Update self.volume_id
if self.volume_id is not None:
if volume.volume_id == self.volume_id and str(volume.delete_time) == "":
return volume
else:
if str(volume.delete_time) == "":
self.volume_id = volume.volume_id
return volume
return None
def create_volume(self):
try:
self.sfe.create_volume(name=self.name,
account_id=self.account_id,
total_size=self.size,
enable512e=self.enable512e,
qos=self.qos,
attributes=self.attributes)
except Exception as err:
self.module.fail_json(msg="Error provisioning volume %s of size %s" % (self.name, self.size),
exception=to_native(err))
def delete_volume(self):
try:
self.sfe.delete_volume(volume_id=self.volume_id)
except Exception as err:
self.module.fail_json(msg="Error deleting volume %s" % self.volume_id,
exception=to_native(err))
def update_volume(self):
try:
self.sfe.modify_volume(self.volume_id,
account_id=self.account_id,
access=self.access,
qos=self.qos,
total_size=self.size,
attributes=self.attributes)
except Exception as err:
self.module.fail_json(msg="Error updating volume %s" % self.name,
exception=to_native(err))
def apply(self):
changed = False
volume_exists = False
update_volume = False
volume_detail = self.get_volume()
if volume_detail:
volume_exists = True
if self.state == 'absent':
# Checking for state change(s) here, and applying it later in the code allows us to support
# check_mode
changed = True
elif self.state == 'present':
if volume_detail.access is not None and self.access is not None and volume_detail.access != self.access:
update_volume = True
changed = True
elif volume_detail.account_id is not None and self.account_id is not None \
and volume_detail.account_id != self.account_id:
update_volume = True
changed = True
elif volume_detail.qos is not None and self.qos is not None and volume_detail.qos != self.qos:
update_volume = True
changed = True
elif volume_detail.total_size is not None and volume_detail.total_size != self.size:
size_difference = abs(float(volume_detail.total_size - self.size))
# Change size only if difference is bigger than 0.001
if size_difference / self.size > 0.001:
update_volume = True
changed = True
elif volume_detail.attributes is not None and self.attributes is not None and \
volume_detail.attributes != self.attributes:
update_volume = True
changed = True
else:
if self.state == 'present':
changed = True
result_message = ""
if changed:
if self.module.check_mode:
result_message = "Check mode, skipping changes"
else:
if self.state == 'present':
if not volume_exists:
self.create_volume()
result_message = "Volume created"
elif update_volume:
self.update_volume()
result_message = "Volume updated"
elif self.state == 'absent':
self.delete_volume()
result_message = "Volume deleted"
self.module.exit_json(changed=changed, msg=result_message)
def main():
v = SolidFireVolume()
v.apply()
if __name__ == '__main__':
main()

View file

@ -622,9 +622,6 @@ plugins/modules/remote_management/cobbler/cobbler_system.py validate-modules:par
plugins/modules/remote_management/dellemc/idrac_server_config_profile.py validate-modules:doc-missing-type
plugins/modules/remote_management/dellemc/idrac_server_config_profile.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/dellemc/ome_device_info.py validate-modules:parameter-list-no-elements
plugins/modules/remote_management/foreman/foreman.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/foreman/katello.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/foreman/katello.py yamllint:unparsable-with-libyaml
plugins/modules/remote_management/hpilo/hpilo_boot.py validate-modules:doc-choices-do-not-match-spec
plugins/modules/remote_management/hpilo/hpilo_boot.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/hpilo/hpilo_info.py validate-modules:parameter-type-not-in-doc
@ -706,7 +703,6 @@ plugins/modules/source_control/git_config.py validate-modules:parameter-type-not
plugins/modules/source_control/github/github_deploy_key.py validate-modules:doc-missing-type
plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-invalid
plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-type-not-in-doc
plugins/modules/source_control/github/github_hooks.py validate-modules:doc-missing-type
plugins/modules/source_control/github/github_issue.py validate-modules:doc-missing-type
plugins/modules/source_control/github/github_issue.py validate-modules:parameter-type-not-in-doc
plugins/modules/source_control/github/github_key.py validate-modules:doc-missing-type
@ -741,41 +737,10 @@ plugins/modules/storage/ibm/ibm_sa_vol.py validate-modules:doc-missing-type
plugins/modules/storage/ibm/ibm_sa_vol.py validate-modules:doc-required-mismatch
plugins/modules/storage/ibm/ibm_sa_vol_map.py validate-modules:doc-missing-type
plugins/modules/storage/ibm/ibm_sa_vol_map.py validate-modules:doc-required-mismatch
plugins/modules/storage/netapp/na_cdot_aggregate.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_aggregate.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_license.py validate-modules:incompatible-default-type
plugins/modules/storage/netapp/na_cdot_license.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_lun.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_lun.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_qtree.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_qtree.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_svm.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_svm.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_user.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_user.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_user_role.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_user_role.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:no-default-for-required-parameter
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:undocumented-parameter
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:parameter-list-no-elements
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:parameter-state-invalid-choice
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_account_manager.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/sf_account_manager.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_check_connections.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_snapshot_schedule_manager.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/sf_snapshot_schedule_manager.py validate-modules:parameter-list-no-elements
plugins/modules/storage/netapp/sf_snapshot_schedule_manager.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_volume_access_group_manager.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/sf_volume_access_group_manager.py validate-modules:parameter-list-no-elements
plugins/modules/storage/netapp/sf_volume_access_group_manager.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:parameter-invalid
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:undocumented-parameter
plugins/modules/storage/purestorage/purefa_facts.py validate-modules:doc-required-mismatch
plugins/modules/storage/purestorage/purefa_facts.py validate-modules:parameter-list-no-elements
plugins/modules/storage/purestorage/purefa_facts.py validate-modules:return-syntax-error

View file

@ -622,9 +622,6 @@ plugins/modules/remote_management/cobbler/cobbler_system.py validate-modules:par
plugins/modules/remote_management/dellemc/idrac_server_config_profile.py validate-modules:doc-missing-type
plugins/modules/remote_management/dellemc/idrac_server_config_profile.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/dellemc/ome_device_info.py validate-modules:parameter-list-no-elements
plugins/modules/remote_management/foreman/foreman.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/foreman/katello.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/foreman/katello.py yamllint:unparsable-with-libyaml
plugins/modules/remote_management/hpilo/hpilo_boot.py validate-modules:doc-choices-do-not-match-spec
plugins/modules/remote_management/hpilo/hpilo_boot.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/hpilo/hpilo_info.py validate-modules:parameter-type-not-in-doc
@ -706,7 +703,6 @@ plugins/modules/source_control/git_config.py validate-modules:parameter-type-not
plugins/modules/source_control/github/github_deploy_key.py validate-modules:doc-missing-type
plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-invalid
plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-type-not-in-doc
plugins/modules/source_control/github/github_hooks.py validate-modules:doc-missing-type
plugins/modules/source_control/github/github_issue.py validate-modules:doc-missing-type
plugins/modules/source_control/github/github_issue.py validate-modules:parameter-type-not-in-doc
plugins/modules/source_control/github/github_key.py validate-modules:doc-missing-type
@ -741,41 +737,10 @@ plugins/modules/storage/ibm/ibm_sa_vol.py validate-modules:doc-missing-type
plugins/modules/storage/ibm/ibm_sa_vol.py validate-modules:doc-required-mismatch
plugins/modules/storage/ibm/ibm_sa_vol_map.py validate-modules:doc-missing-type
plugins/modules/storage/ibm/ibm_sa_vol_map.py validate-modules:doc-required-mismatch
plugins/modules/storage/netapp/na_cdot_aggregate.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_aggregate.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_license.py validate-modules:incompatible-default-type
plugins/modules/storage/netapp/na_cdot_license.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_lun.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_lun.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_qtree.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_qtree.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_svm.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_svm.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_user.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_user.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_user_role.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_user_role.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:no-default-for-required-parameter
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:undocumented-parameter
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:parameter-list-no-elements
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:parameter-state-invalid-choice
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_account_manager.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/sf_account_manager.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_check_connections.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_snapshot_schedule_manager.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/sf_snapshot_schedule_manager.py validate-modules:parameter-list-no-elements
plugins/modules/storage/netapp/sf_snapshot_schedule_manager.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_volume_access_group_manager.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/sf_volume_access_group_manager.py validate-modules:parameter-list-no-elements
plugins/modules/storage/netapp/sf_volume_access_group_manager.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:parameter-invalid
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:undocumented-parameter
plugins/modules/storage/purestorage/purefa_facts.py validate-modules:doc-required-mismatch
plugins/modules/storage/purestorage/purefa_facts.py validate-modules:parameter-list-no-elements
plugins/modules/storage/purestorage/purefa_facts.py validate-modules:return-syntax-error

View file

@ -491,12 +491,6 @@ plugins/modules/remote_management/cobbler/cobbler_sync.py validate-modules:param
plugins/modules/remote_management/cobbler/cobbler_system.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/dellemc/idrac_server_config_profile.py validate-modules:doc-missing-type
plugins/modules/remote_management/dellemc/idrac_server_config_profile.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/foreman/foreman.py validate-modules:deprecation-mismatch
plugins/modules/remote_management/foreman/foreman.py validate-modules:invalid-documentation
plugins/modules/remote_management/foreman/foreman.py validate-modules:missing-main-call
plugins/modules/remote_management/foreman/katello.py validate-modules:deprecation-mismatch
plugins/modules/remote_management/foreman/katello.py validate-modules:invalid-documentation
plugins/modules/remote_management/foreman/katello.py validate-modules:missing-main-call
plugins/modules/remote_management/hpilo/hpilo_boot.py validate-modules:doc-choices-do-not-match-spec
plugins/modules/remote_management/hpilo/hpilo_boot.py validate-modules:parameter-type-not-in-doc
plugins/modules/remote_management/hpilo/hpilo_info.py validate-modules:parameter-type-not-in-doc
@ -559,9 +553,6 @@ plugins/modules/source_control/git_config.py validate-modules:parameter-type-not
plugins/modules/source_control/github/github_deploy_key.py validate-modules:doc-missing-type
plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-invalid
plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-type-not-in-doc
plugins/modules/source_control/github/github_hooks.py validate-modules:deprecation-mismatch
plugins/modules/source_control/github/github_hooks.py validate-modules:invalid-documentation
plugins/modules/source_control/github/github_hooks.py validate-modules:missing-main-call
plugins/modules/source_control/github/github_issue.py validate-modules:doc-missing-type
plugins/modules/source_control/github/github_issue.py validate-modules:parameter-type-not-in-doc
plugins/modules/source_control/github/github_key.py validate-modules:doc-missing-type
@ -581,49 +572,10 @@ plugins/modules/storage/ibm/ibm_sa_host_ports.py validate-modules:doc-missing-ty
plugins/modules/storage/ibm/ibm_sa_pool.py validate-modules:doc-missing-type
plugins/modules/storage/ibm/ibm_sa_vol.py validate-modules:doc-missing-type
plugins/modules/storage/ibm/ibm_sa_vol_map.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_cdot_aggregate.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/na_cdot_aggregate.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/na_cdot_aggregate.py validate-modules:missing-main-call
plugins/modules/storage/netapp/na_cdot_license.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/na_cdot_license.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/na_cdot_license.py validate-modules:missing-main-call
plugins/modules/storage/netapp/na_cdot_lun.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/na_cdot_lun.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/na_cdot_lun.py validate-modules:missing-main-call
plugins/modules/storage/netapp/na_cdot_qtree.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/na_cdot_qtree.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/na_cdot_qtree.py validate-modules:missing-main-call
plugins/modules/storage/netapp/na_cdot_svm.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/na_cdot_svm.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/na_cdot_svm.py validate-modules:missing-main-call
plugins/modules/storage/netapp/na_cdot_user.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/na_cdot_user.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/na_cdot_user.py validate-modules:missing-main-call
plugins/modules/storage/netapp/na_cdot_user_role.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/na_cdot_user_role.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/na_cdot_user_role.py validate-modules:missing-main-call
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/na_cdot_volume.py validate-modules:missing-main-call
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:doc-missing-type
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/na_ontap_gather_facts.py validate-modules:parameter-type-not-in-doc
plugins/modules/storage/netapp/sf_account_manager.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/sf_account_manager.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/sf_account_manager.py validate-modules:missing-main-call
plugins/modules/storage/netapp/sf_check_connections.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/sf_check_connections.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/sf_check_connections.py validate-modules:missing-main-call
plugins/modules/storage/netapp/sf_snapshot_schedule_manager.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/sf_snapshot_schedule_manager.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/sf_snapshot_schedule_manager.py validate-modules:missing-main-call
plugins/modules/storage/netapp/sf_volume_access_group_manager.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/sf_volume_access_group_manager.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/sf_volume_access_group_manager.py validate-modules:missing-main-call
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:deprecation-mismatch
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:invalid-documentation
plugins/modules/storage/netapp/sf_volume_manager.py validate-modules:missing-main-call
plugins/modules/storage/purestorage/purefa_facts.py validate-modules:deprecation-mismatch
plugins/modules/storage/purestorage/purefa_facts.py validate-modules:invalid-documentation
plugins/modules/storage/purestorage/purefa_facts.py validate-modules:return-syntax-error