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:
parent
19b5fceeab
commit
3d66ed3ae3
41 changed files with 58 additions and 5079 deletions
7
.github/BOTMETA.yml
vendored
7
.github/BOTMETA.yml
vendored
|
@ -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:
|
||||
|
|
20
changelogs/fragments/remove-deprecated-modules.yml
Normal file
20
changelogs/fragments/remove-deprecated-modules.yml
Normal 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).
|
|
@ -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
|
||||
|
|
|
@ -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'
|
|
@ -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 = []
|
|
@ -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)
|
|
@ -1 +0,0 @@
|
|||
./remote_management/foreman/foreman.py
|
|
@ -1 +0,0 @@
|
|||
./source_control/github/github_hooks.py
|
|
@ -1 +0,0 @@
|
|||
./remote_management/foreman/katello.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/na_cdot_aggregate.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/na_cdot_license.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/na_cdot_lun.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/na_cdot_qtree.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/na_cdot_svm.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/na_cdot_user.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/na_cdot_user_role.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/na_cdot_volume.py
|
|
@ -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()
|
|
@ -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()
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/sf_account_manager.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/sf_check_connections.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/sf_snapshot_schedule_manager.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/sf_volume_access_group_manager.py
|
|
@ -1 +0,0 @@
|
|||
./storage/netapp/sf_volume_manager.py
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue