summaryrefslogtreecommitdiffstats
path: root/ansible_collections/purestorage/fusion/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/purestorage/fusion/plugins')
-rw-r--r--ansible_collections/purestorage/fusion/plugins/doc_fragments/purestorage.py56
-rw-r--r--ansible_collections/purestorage/fusion/plugins/inventory/__init__.py0
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/errors.py291
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/fusion.py183
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/getters.py99
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/networking.py76
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/operations.py42
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/parsing.py75
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/prerequisites.py162
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/snapshots.py29
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/startup.py26
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_api_client.py140
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_array.py281
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_az.py162
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_hap.py314
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_hw.py88
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_info.py1130
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_ni.py245
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_nig.py276
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_pg.py307
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_pp.py208
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_ra.py284
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_region.py182
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_sc.py257
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_se.py508
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_ss.py211
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_tenant.py171
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_tn.py122
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_ts.py189
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_volume.py559
30 files changed, 0 insertions, 6673 deletions
diff --git a/ansible_collections/purestorage/fusion/plugins/doc_fragments/purestorage.py b/ansible_collections/purestorage/fusion/plugins/doc_fragments/purestorage.py
deleted file mode 100644
index a2f933161..000000000
--- a/ansible_collections/purestorage/fusion/plugins/doc_fragments/purestorage.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2021, Simon Dodsley <simon@purestorage.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
-
-
-class ModuleDocFragment(object):
- # Standard Pure Storage documentation fragment
- DOCUMENTATION = r"""
-options:
- - See separate platform section for more details
-requirements:
- - See separate platform section for more details
-notes:
- - Ansible modules are available for the following Pure Storage products: FlashArray, FlashBlade, Pure1, Fusion
-"""
-
- # Documentation fragment for Fusion
- FUSION = r"""
-options:
- private_key_file:
- aliases: [ key_file ]
- description:
- - Path to the private key file
- - Defaults to the set environment variable under FUSION_PRIVATE_KEY_FILE.
- type: str
- private_key_password:
- description:
- - Password of the encrypted private key file
- type: str
- issuer_id:
- aliases: [ app_id ]
- description:
- - Application ID from Pure1 Registration page
- - eg. pure1:apikey:dssf2331sd
- - Defaults to the set environment variable under FUSION_ISSUER_ID
- type: str
- access_token:
- description:
- - Access token for Fusion Service
- - Defaults to the set environment variable under FUSION_ACCESS_TOKEN
- type: str
-notes:
- - This module requires the I(purefusion) Python library
- - You must set C(FUSION_ISSUER_ID) and C(FUSION_PRIVATE_KEY_FILE) environment variables
- if I(issuer_id) and I(private_key_file) arguments are not passed to the module directly
- - If you want to use access token for authentication, you must use C(FUSION_ACCESS_TOKEN) environment variable
- if I(access_token) argument is not passed to the module directly
-requirements:
- - python >= 3.8
- - purefusion
-"""
diff --git a/ansible_collections/purestorage/fusion/plugins/inventory/__init__.py b/ansible_collections/purestorage/fusion/plugins/inventory/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/ansible_collections/purestorage/fusion/plugins/inventory/__init__.py
+++ /dev/null
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/errors.py b/ansible_collections/purestorage/fusion/plugins/module_utils/errors.py
deleted file mode 100644
index f3d574edc..000000000
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/errors.py
+++ /dev/null
@@ -1,291 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# (c) 2023, Jan Kodera (jkodera@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-try:
- import fusion as purefusion
- import urllib3
-except ImportError:
- pass
-
-import sys
-import json
-import re
-import traceback as trace
-
-
-class OperationException(Exception):
- """Raised if an asynchronous Operation fails."""
-
- def __init__(self, op, http_error=None):
- self._op = op
- self._http_error = http_error
-
- @property
- def op(self):
- return self._op
-
- @property
- def http_error(self):
- return self._http_error
-
-
-def _get_verbosity(module):
- # verbosity is a private member and Ansible does not really allow
- # providing extra information only if the user wants it due to ideological
- # reasons, so extract it as carefully as possible and assume non-verbose
- # if something fails
- try:
- if module._verbosity is not None and isinstance(module._verbosity, int):
- return module._verbosity
- except Exception:
- pass
- return 0
-
-
-def _extract_rest_call_site(traceback):
- # extracts first function in traceback that comes from 'fusion.api.*_api*',
- # converts its name from something like 'get_volume' to 'Get volume' and returns
- while traceback:
- try:
- frame = traceback.tb_frame
- func_name = (
- frame.f_code.co_name
- ) # contains function name, e.g. 'get_volume'
- mod_path = frame.f_globals[
- "__name__"
- ] # contains module path, e.g. 'fusion.api.volumes_api'
- path_segments = mod_path.split(".")
- if (
- path_segments[0] == "fusion"
- and path_segments[1] == "api"
- and "_api" in path_segments[2]
- ):
- call_site = func_name.replace("_", " ").capitalize()
- return call_site
- except Exception:
- pass
- traceback = traceback.tb_next
- return None
-
-
-class DetailsPrinter:
- def __init__(self, target):
- self._target = target
- self._parenthesed = False
-
- def append(self, what):
- if not self._parenthesed:
- self._target += " ("
- self._parenthesed = True
- else:
- self._target += ", "
-
- self._target += what
-
- def finish(self):
- if self._parenthesed:
- self._target += ")"
- return self._target
-
-
-def format_fusion_api_exception(exception, traceback=None):
- """Formats `fusion.rest.ApiException` into a simple short form, suitable
- for Ansible error output. Returns a (message: str, body: dict) tuple."""
- message = None
- code = None
- resource_name = None
- request_id = None
- body = None
- call_site = _extract_rest_call_site(traceback)
- try:
- body = json.loads(exception.body)
- request_id = body.get("request_id", None)
- error = body["error"]
- message = error.get("message")
- code = error.get("pure_code")
- if not code:
- code = exception.status
- if not code:
- code = error.get("http_code")
- resource_name = error["details"]["name"]
- except Exception:
- pass
-
- output = ""
- if call_site:
- output += "'{0}' failed".format(call_site)
- else:
- output += "request failed"
-
- if message:
- output += ", {0}".format(message.replace('"', "'"))
-
- details = DetailsPrinter(output)
- if resource_name:
- details.append("resource: '{0}'".format(resource_name))
- if code:
- details.append("code: '{0}'".format(code))
- if request_id:
- details.append("request id: '{0}'".format(request_id))
- output = details.finish()
-
- return (output, body)
-
-
-def format_failed_fusion_operation_exception(exception):
- """Formats failed `fusion.Operation` into a simple short form, suitable
- for Ansible error output. Returns a (message: str, body: dict) tuple."""
- op = exception.op
- http_error = exception.http_error
- if op.status != "Failed" and not http_error:
- raise ValueError(
- "BUG: can only format Operation exception with .status == Failed or http_error != None"
- )
-
- message = None
- code = None
- operation_name = None
- operation_id = None
-
- try:
- if op.status == "Failed":
- operation_id = op.id
- error = op.error
- message = error.message
- code = error.pure_code
- if not code:
- code = error.http_code
- operation_name = op.request_type
- except Exception:
- pass
-
- output = ""
- if operation_name:
- # converts e.g. 'CreateVolume' to 'Create volume'
- operation_name = re.sub("(.)([A-Z][a-z]+)", r"\1 \2", operation_name)
- operation_name = re.sub(
- "([a-z0-9])([A-Z])", r"\1 \2", operation_name
- ).capitalize()
- output += "{0}: ".format(operation_name)
- output += "operation failed"
-
- if message:
- output += ", {0}".format(message.replace('"', "'"))
-
- details = DetailsPrinter(output)
- if code:
- details.append("code: '{0}'".format(code))
- if operation_id:
- details.append("operation id: '{0}'".format(operation_id))
- if http_error:
- details.append("HTTP error: '{0}'".format(str(http_error).replace('"', "'")))
-
- output = details.finish()
-
- return output
-
-
-def format_http_exception(exception, traceback):
- """Formats failed `urllib3.exceptions` exceptions into a simple short form,
- suitable for Ansible error output. Returns a `str`."""
- # urllib3 exceptions hide all details in a formatted message so all we
- # can do is append the REST call that caused this
- output = ""
- call_site = _extract_rest_call_site(traceback)
- if call_site:
- output += "'{0}': ".format(call_site)
- output += "HTTP request failed via "
-
- inner = exception
- while True:
- try:
- e = inner.reason
- if e and isinstance(e, urllib3.exceptions.HTTPError):
- inner = e
- continue
- break
- except Exception:
- break
-
- if inner != exception:
- output += "'{0}'/'{1}'".format(type(inner).__name__, type(exception).__name__)
- else:
- output += "'{0}'".format(type(exception).__name__)
-
- output += " - {0}".format(str(exception).replace('"', "'"))
-
- return output
-
-
-def _handle_api_exception(
- module,
- exception,
- traceback,
- verbosity,
-):
- (error_message, body) = format_fusion_api_exception(exception, traceback)
-
- if verbosity > 1:
- module.fail_json(msg=error_message, call_details=body, traceback=str(traceback))
- elif verbosity > 0:
- module.fail_json(msg=error_message, call_details=body)
- else:
- module.fail_json(msg=error_message)
-
-
-def _handle_operation_exception(module, exception, traceback, verbosity):
- op = exception.op
-
- error_message = format_failed_fusion_operation_exception(exception)
-
- if verbosity > 1:
- module.fail_json(
- msg=error_message, op_details=op.to_dict(), traceback=str(traceback)
- )
- elif verbosity > 0:
- module.fail_json(msg=error_message, op_details=op.to_dict())
- else:
- module.fail_json(msg=error_message)
-
-
-def _handle_http_exception(module, exception, traceback, verbosity):
- error_message = format_http_exception(exception, traceback)
-
- if verbosity > 1:
- module.fail_json(msg=error_message, traceback=trace.format_exception(exception))
- else:
- module.fail_json(msg=error_message)
-
-
-def _except_hook_callback(module, original_hook, type, value, traceback):
- verbosity = _get_verbosity(module)
- if type == purefusion.rest.ApiException:
- _handle_api_exception(
- module,
- value,
- traceback,
- verbosity,
- )
- elif type == OperationException:
- _handle_operation_exception(module, value, traceback, verbosity)
- elif issubclass(type, urllib3.exceptions.HTTPError):
- _handle_http_exception(module, value, traceback, verbosity)
-
- # if we bubbled here the handlers were not able to process the exception
- original_hook(type, value, traceback)
-
-
-def install_fusion_exception_hook(module):
- """Installs a hook that catches `purefusion.rest.ApiException` and
- `OperationException` and produces simpler and nicer error messages
- for Ansible output."""
- original_hook = sys.excepthook
- sys.excepthook = lambda type, value, traceback: _except_hook_callback(
- module, original_hook, type, value, traceback
- )
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/fusion.py b/ansible_collections/purestorage/fusion/plugins/module_utils/fusion.py
deleted file mode 100644
index 74b5f0e91..000000000
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/fusion.py
+++ /dev/null
@@ -1,183 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# This code is part of Ansible, but is an independent component.
-# This particular file snippet, and this file snippet only, is BSD licensed.
-# Modules you write using this snippet, which is embedded dynamically by Ansible
-# still belong to the author of the module, and may assign their own license
-# to the complete work.
-#
-# Copyright (c), Simon Dodsley <simon@purestorage.com>,2021
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without modification,
-# are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-try:
- import fusion
-except ImportError:
- pass
-
-from os import environ
-from urllib.parse import urljoin
-import platform
-
-TOKEN_EXCHANGE_URL = "https://api.pure1.purestorage.com/oauth2/1.0/token"
-VERSION = 1.0
-USER_AGENT_BASE = "Ansible"
-
-PARAM_ISSUER_ID = "issuer_id"
-PARAM_PRIVATE_KEY_FILE = "private_key_file"
-PARAM_PRIVATE_KEY_PASSWORD = "private_key_password"
-PARAM_ACCESS_TOKEN = "access_token"
-ENV_ISSUER_ID = "FUSION_ISSUER_ID"
-ENV_API_HOST = "FUSION_API_HOST"
-ENV_PRIVATE_KEY_FILE = "FUSION_PRIVATE_KEY_FILE"
-ENV_TOKEN_ENDPOINT = "FUSION_TOKEN_ENDPOINT"
-ENV_ACCESS_TOKEN = "FUSION_ACCESS_TOKEN"
-
-# will be deprecated in 2.0.0
-PARAM_APP_ID = "app_id" # replaced by PARAM_ISSUER_ID
-PARAM_KEY_FILE = "key_file" # replaced by PARAM_PRIVATE_KEY_FILE
-ENV_APP_ID = "FUSION_APP_ID" # replaced by ENV_ISSUER_ID
-ENV_HOST = "FUSION_HOST" # replaced by ENV_API_HOST
-DEP_VER = "2.0.0"
-BASE_PATH = "/api/1.1"
-
-
-def _env_deprecation_warning(module, old_env, new_env, vers):
- if old_env in environ:
- if new_env in environ:
- module.warn(
- f"{old_env} env variable is ignored because {new_env} is specified."
- f" {old_env} env variable is deprecated and will be removed in version {vers}"
- f" Please use {new_env} env variable only."
- )
- else:
- module.warn(
- f"{old_env} env variable is deprecated and will be removed in version {vers}"
- f" Please use {new_env} env variable instead."
- )
-
-
-def _param_deprecation_warning(module, old_param, new_param, vers):
- if old_param in module.params:
- module.warn(
- f"{old_param} parameter is deprecated and will be removed in version {vers}"
- f" Please use {new_param} parameter instead."
- f" Don't use both parameters simultaneously."
- )
-
-
-def get_fusion(module):
- """Return System Object or Fail"""
- # deprecation warnings
- _param_deprecation_warning(module, PARAM_APP_ID, PARAM_ISSUER_ID, DEP_VER)
- _param_deprecation_warning(module, PARAM_KEY_FILE, PARAM_PRIVATE_KEY_FILE, DEP_VER)
- _env_deprecation_warning(module, ENV_APP_ID, ENV_ISSUER_ID, DEP_VER)
- _env_deprecation_warning(module, ENV_HOST, ENV_API_HOST, DEP_VER)
-
- user_agent = "%(base)s %(class)s/%(version)s (%(platform)s)" % {
- "base": USER_AGENT_BASE,
- "class": __name__,
- "version": VERSION,
- "platform": platform.platform(),
- }
-
- issuer_id = module.params[PARAM_ISSUER_ID]
- access_token = module.params[PARAM_ACCESS_TOKEN]
- private_key_file = module.params[PARAM_PRIVATE_KEY_FILE]
- private_key_password = module.params[PARAM_PRIVATE_KEY_PASSWORD]
-
- if private_key_password is not None:
- module.fail_on_missing_params([PARAM_PRIVATE_KEY_FILE])
-
- config = fusion.Configuration()
- if ENV_API_HOST in environ or ENV_HOST in environ:
- host_url = environ.get(ENV_API_HOST, environ.get(ENV_HOST))
- config.host = urljoin(host_url, BASE_PATH)
- config.token_endpoint = environ.get(ENV_TOKEN_ENDPOINT, config.token_endpoint)
-
- if access_token is not None:
- config.access_token = access_token
- elif issuer_id is not None and private_key_file is not None:
- config.issuer_id = issuer_id
- config.private_key_file = private_key_file
- if private_key_password is not None:
- config.private_key_password = private_key_password
- elif ENV_ACCESS_TOKEN in environ:
- config.access_token = environ.get(ENV_ACCESS_TOKEN)
- elif (
- ENV_ISSUER_ID in environ or ENV_APP_ID in environ
- ) and ENV_PRIVATE_KEY_FILE in environ:
- config.issuer_id = environ.get(ENV_ISSUER_ID, environ.get(ENV_APP_ID))
- config.private_key_file = environ.get(ENV_PRIVATE_KEY_FILE)
- else:
- module.fail_json(
- msg=f"You must set either {ENV_ISSUER_ID} and {ENV_PRIVATE_KEY_FILE} or {ENV_ACCESS_TOKEN} environment variables. "
- f"Or module arguments either {PARAM_ISSUER_ID} and {PARAM_PRIVATE_KEY_FILE} or {PARAM_ACCESS_TOKEN}"
- )
-
- try:
- client = fusion.ApiClient(config)
- client.set_default_header("User-Agent", user_agent)
- api_instance = fusion.DefaultApi(client)
- api_instance.get_version()
- except Exception as err:
- module.fail_json(msg="Fusion authentication failed: {0}".format(err))
-
- return client
-
-
-def fusion_argument_spec():
- """Return standard base dictionary used for the argument_spec argument in AnsibleModule"""
-
- return {
- PARAM_ISSUER_ID: {
- "no_log": True,
- "aliases": [PARAM_APP_ID],
- "deprecated_aliases": [
- {
- "name": PARAM_APP_ID,
- "version": DEP_VER,
- "collection_name": "purefusion.fusion",
- }
- ],
- },
- PARAM_PRIVATE_KEY_FILE: {
- "no_log": False,
- "aliases": [PARAM_KEY_FILE],
- "deprecated_aliases": [
- {
- "name": PARAM_KEY_FILE,
- "version": DEP_VER,
- "collection_name": "purefusion.fusion",
- }
- ],
- },
- PARAM_PRIVATE_KEY_PASSWORD: {
- "no_log": True,
- },
- PARAM_ACCESS_TOKEN: {
- "no_log": True,
- },
- }
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/getters.py b/ansible_collections/purestorage/fusion/plugins/module_utils/getters.py
deleted file mode 100644
index 535de76ba..000000000
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/getters.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# (c) 2023, Daniel Turecek (dturecek@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-
-def get_array(module, fusion, array_name=None):
- """Return Array or None"""
- array_api_instance = purefusion.ArraysApi(fusion)
- try:
- if array_name is None:
- array_name = module.params["array"]
-
- return array_api_instance.get_array(
- array_name=array_name,
- availability_zone_name=module.params["availability_zone"],
- region_name=module.params["region"],
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def get_az(module, fusion, availability_zone_name=None):
- """Get Availability Zone or None"""
- az_api_instance = purefusion.AvailabilityZonesApi(fusion)
- try:
- if availability_zone_name is None:
- availability_zone_name = module.params["availability_zone"]
-
- return az_api_instance.get_availability_zone(
- region_name=module.params["region"],
- availability_zone_name=availability_zone_name,
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def get_region(module, fusion, region_name=None):
- """Get Region or None"""
- region_api_instance = purefusion.RegionsApi(fusion)
- try:
- if region_name is None:
- region_name = module.params["region"]
-
- return region_api_instance.get_region(
- region_name=region_name,
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def get_ss(module, fusion, storage_service_name=None):
- """Return Storage Service or None"""
- ss_api_instance = purefusion.StorageServicesApi(fusion)
- try:
- if storage_service_name is None:
- storage_service_name = module.params["storage_service"]
-
- return ss_api_instance.get_storage_service(
- storage_service_name=storage_service_name
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def get_tenant(module, fusion, tenant_name=None):
- """Return Tenant or None"""
- api_instance = purefusion.TenantsApi(fusion)
- try:
- if tenant_name is None:
- tenant_name = module.params["tenant"]
-
- return api_instance.get_tenant(tenant_name=tenant_name)
- except purefusion.rest.ApiException:
- return None
-
-
-def get_ts(module, fusion, tenant_space_name=None):
- """Tenant Space or None"""
- ts_api_instance = purefusion.TenantSpacesApi(fusion)
- try:
- if tenant_space_name is None:
- tenant_space_name = module.params["tenant_space"]
-
- return ts_api_instance.get_tenant_space(
- tenant_name=module.params["tenant"],
- tenant_space_name=tenant_space_name,
- )
- except purefusion.rest.ApiException:
- return None
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/networking.py b/ansible_collections/purestorage/fusion/plugins/module_utils/networking.py
deleted file mode 100644
index a00d8200a..000000000
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/networking.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# (c) 2023, Jan Kodera (jkodera@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-import ipaddress
-
-# while regexes are hard to maintain, they are used anyways for few reasons:
-# a) REST backend accepts fairly restricted input and we need to match that input instead of all
-# the esoteric extra forms various packages are usually capable of parsing (like dotted-decimal
-# subnet masks, octal octets, hexadecimal octets, zero-extended addresses etc.)
-# b) manually written parsing routines are usually complex to write, verify and think about
-import re
-
-# IPv4 octet regex part, matches only simple decimal 0-255 without leading zeroes
-_octet = (
- "((?:[0-9])|" # matches 0-9
- "(?:[1-9][0-9])|" # matches 10-99
- "(?:1[0-9][0-9])|" # matches 100-199
- "(?:2[0-4][0-9])|" # matches 200-249
- "(?:25[0-5]))" # matches 250-255
-)
-
-# IPv4 subnet mask regex part, matches decimal 8-32
-_subnet_mask = (
- "((?:[8-9])|" # matches 8-9
- "(?:[1-2][0-9])|" # matches 10-29
- "(?:3[0-2]))" # matches 30-32
-)
-
-# matches IPv4 addresses
-_addr_pattern = re.compile(r"^{octet}\.{octet}\.{octet}\.{octet}$".format(octet=_octet))
-# matches IPv4 networks in CIDR format, i.e. addresses in the form 'a.b.c.d/e'
-_cidr_pattern = re.compile(
- r"^{octet}\.{octet}\.{octet}\.{octet}\/{0}$".format(_subnet_mask, octet=_octet)
-)
-
-
-def is_valid_network(addr):
- """Returns True if `addr` is IPv4 address/submask in bit CIDR notation, False otherwise."""
- match = re.match(_cidr_pattern, addr)
- if match is None:
- return False
- for i in range(4):
- if int(match.group(i + 1)) > 255:
- return False
- mask = int(match.group(5))
- if mask < 8 or mask > 32:
- return False
- return True
-
-
-def is_valid_address(addr):
- """Returns True if `addr` is a valid IPv4 address, False otherwise. Does not support
- octal/hex notations."""
- match = re.match(_addr_pattern, addr)
- if match is None:
- return False
- for i in range(4):
- if int(match.group(i + 1)) > 255:
- return False
- return True
-
-
-def is_address_in_network(addr, network):
- """Returns True if `addr` and `network` are a valid IPv4 address and
- IPv4 network respectively and if `addr` is in `network`, False otherwise."""
- if not is_valid_address(addr) or not is_valid_network(network):
- return False
- parsed_addr = ipaddress.ip_address(addr)
- parsed_net = ipaddress.ip_network(network)
- return parsed_addr in parsed_net
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/operations.py b/ansible_collections/purestorage/fusion/plugins/module_utils/operations.py
deleted file mode 100644
index dc80aefe3..000000000
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/operations.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# (c) 2023, Jan Kodera (jkodera@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-import time
-import math
-
-try:
- import fusion as purefusion
- from urllib3.exceptions import HTTPError
-except ImportError:
- pass
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.errors import (
- OperationException,
-)
-
-
-def await_operation(fusion, operation, fail_playbook_if_operation_fails=True):
- """
- Waits for given operation to finish.
- Throws an exception by default if the operation fails.
- """
- op_api = purefusion.OperationsApi(fusion)
- operation_get = None
- while True:
- try:
- operation_get = op_api.get_operation(operation.id)
- if operation_get.status == "Succeeded":
- return operation_get
- if operation_get.status == "Failed":
- if fail_playbook_if_operation_fails:
- raise OperationException(operation_get)
- return operation_get
- except HTTPError as err:
- raise OperationException(operation, http_error=err)
- time.sleep(int(math.ceil(operation_get.retry_in / 1000)))
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/parsing.py b/ansible_collections/purestorage/fusion/plugins/module_utils/parsing.py
deleted file mode 100644
index 1bcb8b812..000000000
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/parsing.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# (c) 2023, Jan Kodera (jkodera@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-import re
-
-__metaclass__ = type
-
-METRIC_SUFFIXES = ["K", "M", "G", "T", "P"]
-
-duration_pattern = re.compile(
- r"^((?P<Y>\d+)Y)?((?P<W>\d+)W)?((?P<D>\d+)D)?(((?P<H>\d+)H)?((?P<M>\d+)M)?)?$"
-)
-duration_transformation = {
- "Y": 365 * 24 * 60,
- "W": 7 * 24 * 60,
- "D": 24 * 60,
- "H": 60,
- "M": 1,
-}
-
-
-def parse_number_with_metric_suffix(module, number, factor=1024):
- """Given a human-readable string (e.g. 2G, 30M, 400),
- return the resolved integer.
- Will call `module.fail_json()` for invalid inputs.
- """
- try:
- stripped_num = number.strip()
- if stripped_num[-1].isdigit():
- return int(stripped_num)
- # has unit prefix
- result = float(stripped_num[:-1])
- suffix = stripped_num[-1].upper()
- factor_count = METRIC_SUFFIXES.index(suffix) + 1
- for _i in range(0, factor_count):
- result = result * float(factor)
- return int(result)
- except Exception:
- module.fail_json(
- msg="'{0}' is not a valid number, use '400', '1K', '2M', ...".format(number)
- )
- return 0
-
-
-def parse_duration(period):
- if period.isdigit():
- return int(period)
-
- match = duration_pattern.match(period.upper())
- if not match or period == "":
- raise ValueError("Invalid format")
-
- durations = {
- "Y": int(match.group("Y")) if match.group("Y") else 0,
- "W": int(match.group("W")) if match.group("W") else 0,
- "D": int(match.group("D")) if match.group("D") else 0,
- "H": int(match.group("H")) if match.group("H") else 0,
- "M": int(match.group("M")) if match.group("M") else 0,
- }
- return sum(value * duration_transformation[key] for key, value in durations.items())
-
-
-def parse_minutes(module, period):
- try:
- return parse_duration(period)
- except ValueError:
- module.fail_json(
- msg=(
- "'{0}' is not a valid time period, use combination of data units (Y,W,D,H,M)"
- "e.g. 4W3D5H, 5D8H5M, 3D, 5W, 1Y5W..."
- ).format(period)
- )
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/prerequisites.py b/ansible_collections/purestorage/fusion/plugins/module_utils/prerequisites.py
deleted file mode 100644
index db00a9c6f..000000000
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/prerequisites.py
+++ /dev/null
@@ -1,162 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# (c) 2023, Jan Kodera (jkodera@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-import re
-import importlib
-import importlib.metadata
-
-# This file exists because Ansible currently cannot declare dependencies on Python modules.
-# see https://github.com/ansible/ansible/issues/62733 for more info about lack of req support
-
-#############################
-
-# 'module_name, package_name, version_requirements' triplets
-DEPENDENCIES = [
- ("fusion", "purefusion", ">=1.0.11,<2.0"),
- ("urllib3", "urllib3", None),
-]
-
-#############################
-
-
-def _parse_version(val):
- """
- Parse a package version.
- Takes in either MAJOR.MINOR or MAJOR.MINOR.PATCH form. PATCH
- can have additional suffixes, e.g. '-prerelease', 'a1', ...
-
- :param val: a string representation of the package version
- :returns: tuple of ints (MAJOR, MINOR, PATCH) or None if not parsed
- """
- # regexes for this were really ugly
- try:
- parts = val.split(".")
- if len(parts) < 2 or len(parts) > 3:
- return None
- major = int(parts[0])
- minor = int(parts[1])
- if len(parts) > 2:
- patch = re.match(r"^\d+", parts[2])
- patch = int(patch.group(0))
- else:
- patch = None
- return (major, minor, patch)
- except Exception:
- return None
-
-
-# returns list of tuples [(COMPARATOR, (MAJOR, MINOR, PATCH)),...]
-def _parse_version_requirements(val):
- """
- Parse package requirements.
-
- :param val: a string in the form ">=1.0.11,<2.0"
- :returns: list of tuples in the form [(">=", (1, 0, 11)), ("<", (2, 0, None))] or None if not parsed
- """
- reqs = []
- try:
- parts = val.split(",")
- for part in parts:
- match = re.match(r"\s*(>=|<=|==|=|<|>|!=)\s*([^\s]+)", part)
- op = match.group(1)
- ver = match.group(2)
- ver_tuple = _parse_version(ver)
- if not ver_tuple:
- raise ValueError("invalid version {0}".format(ver))
- reqs.append((op, ver_tuple))
- return reqs
- except Exception as e:
- raise ValueError("invalid version requirement '{0}' {1}".format(val, e))
-
-
-def _compare_version(op, ver, req):
- """
- Compare two versions.
-
- :param op: a string, one of comparators ">=", "<=", "=", "==", ">" or "<"
- :param ver: version tuple in _parse_version() return form
- :param req: version tuple in _parse_version() return form
- :returns: True if ver 'op' req; False otherwise
- """
-
- def _cmp(a, b):
- return (a > b) - (a < b)
-
- major = _cmp(ver[0], req[0])
- minor = _cmp(ver[1], req[1])
- patch = None
- if req[2] is not None:
- patch = _cmp(ver[2] or 0, req[2])
- result = {
- ">=": major > 0 or (major == 0 and (minor > 0 or patch is None or patch >= 0)),
- "<=": major < 0 or (major == 0 and (minor < 0 or patch is None or patch <= 0)),
- ">": major > 0
- or (major == 0 and (minor > 0 or patch is not None and patch > 0)),
- "<": major < 0
- or (major == 0 and (minor < 0 or patch is not None and patch < 0)),
- "=": major == 0 and minor == 0 and (patch is None or patch == 0),
- "==": major == 0 and minor == 0 and (patch is None or patch == 0),
- "!=": major != 0 or minor != 0 or (patch is not None and patch != 0),
- }.get(op)
- return result
-
-
-def _version_satisfied(version, requirements):
- """
- Checks whether version matches given version requirements.
-
- :param version: a string, in input form to _parse_version()
- :param requirements: as string, in input form to _parse_version_requirements()
- :returns: True if 'version' matches 'requirements'; False otherwise
- """
-
- version = _parse_version(version)
- requirements = _parse_version_requirements(requirements)
- for req in requirements:
- if not _compare_version(req[0], version, req[1]):
- return False
- return True
-
-
-# poor helper to work around the fact Ansible is unable to manage python dependencies
-def _check_import(ansible_module, module, package=None, version_requirements=None):
- """
- Tries to import a module and optionally validates its package version.
- Calls AnsibleModule.fail_json() if not satisfied.
-
- :param ansible_module: an AnsibleModule instance
- :param module: a string with module name to try to import
- :param package: a string, package to check version for; must be specified with 'version_requirements'
- :param version_requirements: a string, version requirements for 'package'
- """
- try:
- importlib.import_module(module)
- except ImportError:
- ansible_module.fail_json(
- msg="Error: Python package '{0}' required and missing".format(module)
- )
-
- if package and version_requirements:
- # silently ignore version checks and hope for the best if we can't fetch
- # the package version since we can't know how the user installs packages
- try:
- version = importlib.metadata.version(package)
- if version and not _version_satisfied(version, version_requirements):
- ansible_module.fail_json(
- msg="Error: Python package '{0}' version '{1}' does not satisfy requirements '{2}'".format(
- module, version, version_requirements
- )
- )
- except Exception:
- pass # ignore package loads
-
-
-def check_dependencies(ansible_module):
- for module, package, version_requirements in DEPENDENCIES:
- _check_import(ansible_module, module, package, version_requirements)
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/snapshots.py b/ansible_collections/purestorage/fusion/plugins/module_utils/snapshots.py
deleted file mode 100644
index ed34c1c0e..000000000
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/snapshots.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-
-
-def delete_snapshot(fusion, snap, snapshots_api):
- patch = purefusion.SnapshotPatch(destroyed=purefusion.NullableBoolean(True))
- op = snapshots_api.update_snapshot(
- body=patch,
- tenant_name=snap.tenant.name,
- tenant_space_name=snap.tenant_space.name,
- snapshot_name=snap.name,
- )
- await_operation(fusion, op)
- op = snapshots_api.delete_snapshot(
- tenant_name=snap.tenant.name,
- tenant_space_name=snap.tenant_space.name,
- snapshot_name=snap.name,
- )
- await_operation(fusion, op)
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/startup.py b/ansible_collections/purestorage/fusion/plugins/module_utils/startup.py
deleted file mode 100644
index 55d7f11a2..000000000
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/startup.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# (c) 2023, Jan Kodera (jkodera@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.errors import (
- install_fusion_exception_hook,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.prerequisites import (
- check_dependencies,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- get_fusion,
-)
-
-
-def setup_fusion(module):
- check_dependencies(module)
- install_fusion_exception_hook(module)
- return get_fusion(module)
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_api_client.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_api_client.py
deleted file mode 100644
index 42254338f..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_api_client.py
+++ /dev/null
@@ -1,140 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_api_client
-version_added: '1.0.0'
-short_description: Manage API clients in Pure Storage Fusion
-description:
-- Create or delete an API Client in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the client.
- type: str
- required: true
- state:
- description:
- - Define whether the client should exist or not.
- default: present
- choices: [ present, absent ]
- type: str
- public_key:
- description:
- - The API clients PEM formatted (Base64 encoded) RSA public key.
- - Include the C(—–BEGIN PUBLIC KEY—–) and C(—–END PUBLIC KEY—–) lines.
- type: str
- required: true
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new API client foo
- purestorage.fusion.fusion_api_client:
- name: "foo client"
- public_key: "{{lookup('file', 'public_pem_file') }}"
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-
-
-def get_client_id(module, fusion):
- """Get API Client ID, or None if not available"""
- id_api_instance = purefusion.IdentityManagerApi(fusion)
- try:
- clients = id_api_instance.list_api_clients()
- for client in clients:
- if (
- client.public_key == module.params["public_key"]
- and client.display_name == module.params["name"]
- ):
- return client.id
- return None
- except purefusion.rest.ApiException:
- return None
-
-
-def delete_client(module, fusion, client_id):
- """Delete API Client"""
- id_api_instance = purefusion.IdentityManagerApi(fusion)
-
- changed = True
- if not module.check_mode:
- id_api_instance.delete_api_client(api_client_id=client_id)
- module.exit_json(changed=changed)
-
-
-def create_client(module, fusion):
- """Create API Client"""
-
- id_api_instance = purefusion.IdentityManagerApi(fusion)
-
- changed = True
- id = None
- if not module.check_mode:
- client = purefusion.APIClientPost(
- public_key=module.params["public_key"],
- display_name=module.params["name"],
- )
- res = id_api_instance.create_api_client(client)
- id = res.id
- module.exit_json(changed=changed, id=id)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- public_key=dict(type="str", required=True),
- state=dict(type="str", default="present", choices=["present", "absent"]),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- client_id = get_client_id(module, fusion)
- if client_id is None and state == "present":
- create_client(module, fusion)
- elif client_id is not None and state == "absent":
- delete_client(module, fusion, client_id)
- if client_id is not None:
- module.exit_json(changed=False, id=client_id)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_array.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_array.py
deleted file mode 100644
index ec94d616f..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_array.py
+++ /dev/null
@@ -1,281 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_array
-version_added: '1.0.0'
-short_description: Manage arrays in Pure Storage Fusion
-description:
-- Create or delete an array in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the array.
- type: str
- required: true
- state:
- description:
- - Define whether the array should exist or not.
- default: present
- choices: [ present, absent ]
- type: str
- display_name:
- description:
- - The human name of the array.
- - If not provided, defaults to I(name).
- type: str
- region:
- description:
- - The region the AZ is in.
- type: str
- required: true
- availability_zone:
- aliases: [ az ]
- description:
- - The availability zone the array is located in.
- type: str
- required: true
- hardware_type:
- description:
- - Hardware type to which the storage class applies.
- choices: [ flash-array-x, flash-array-c, flash-array-x-optane, flash-array-xl ]
- type: str
- host_name:
- description:
- - Management IP address of the array, or FQDN.
- type: str
- appliance_id:
- description:
- - Appliance ID of the array.
- type: str
- apartment_id:
- description:
- - The Apartment ID of the Array.
- type: str
- maintenance_mode:
- description:
- - "Switch the array into maintenance mode or back.
- Array in maintenance mode can have placement groups migrated out but not in.
- Intended use cases are for example safe decommissioning or to prevent use
- of an array that has not yet been fully configured."
- type: bool
- unavailable_mode:
- description:
- - "Switch the array into unavailable mode or back.
- Fusion tries to exclude unavailable arrays from virtually any operation it
- can. This is to prevent stalling operations in case of e.g. a networking
- failure. As of the moment arrays have to be marked unavailable manually."
- type: bool
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new array foo
- purestorage.fusion.fusion_array:
- name: foo
- az: zone_1
- region: region1
- hardware_type: flash-array-x
- host_name: foo_array
- display_name: "foo array"
- appliance_id: 1227571-198887878-35016350232000707
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils import getters
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-
-
-def get_array(module, fusion):
- """Return Array or None"""
- return getters.get_array(module, fusion, array_name=module.params["name"])
-
-
-def create_array(module, fusion):
- """Create Array"""
-
- array_api_instance = purefusion.ArraysApi(fusion)
- id = None
-
- if not module.check_mode:
- if not module.params["display_name"]:
- display_name = module.params["name"]
- else:
- display_name = module.params["display_name"]
- array = purefusion.ArrayPost(
- hardware_type=module.params["hardware_type"],
- display_name=display_name,
- host_name=module.params["host_name"],
- name=module.params["name"],
- appliance_id=module.params["appliance_id"],
- apartment_id=module.params["apartment_id"],
- )
- res = array_api_instance.create_array(
- array,
- availability_zone_name=module.params["availability_zone"],
- region_name=module.params["region"],
- )
- res_op = await_operation(fusion, res)
- id = res_op.result.resource.id
-
- return True, id
-
-
-def update_array(module, fusion):
- """Update Array"""
- array = get_array(module, fusion)
- patches = []
- if (
- module.params["display_name"]
- and module.params["display_name"] != array.display_name
- ):
- patch = purefusion.ArrayPatch(
- display_name=purefusion.NullableString(module.params["display_name"]),
- )
- patches.append(patch)
-
- if module.params["host_name"] and module.params["host_name"] != array.host_name:
- patch = purefusion.ArrayPatch(
- host_name=purefusion.NullableString(module.params["host_name"])
- )
- patches.append(patch)
-
- if (
- module.params["maintenance_mode"] is not None
- and module.params["maintenance_mode"] != array.maintenance_mode
- ):
- patch = purefusion.ArrayPatch(
- maintenance_mode=purefusion.NullableBoolean(
- module.params["maintenance_mode"]
- )
- )
- patches.append(patch)
- if (
- module.params["unavailable_mode"] is not None
- and module.params["unavailable_mode"] != array.unavailable_mode
- ):
- patch = purefusion.ArrayPatch(
- unavailable_mode=purefusion.NullableBoolean(
- module.params["unavailable_mode"]
- )
- )
- patches.append(patch)
-
- if not module.check_mode:
- array_api_instance = purefusion.ArraysApi(fusion)
- for patch in patches:
- op = array_api_instance.update_array(
- patch,
- availability_zone_name=module.params["availability_zone"],
- region_name=module.params["region"],
- array_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- changed = len(patches) != 0
- return changed
-
-
-def delete_array(module, fusion):
- """Delete Array - not currently available"""
- array_api_instance = purefusion.ArraysApi(fusion)
- if not module.check_mode:
- res = array_api_instance.delete_array(
- region_name=module.params["region"],
- availability_zone_name=module.params["availability_zone"],
- array_name=module.params["name"],
- )
- await_operation(fusion, res)
- return True
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- availability_zone=dict(type="str", required=True, aliases=["az"]),
- display_name=dict(type="str"),
- region=dict(type="str", required=True),
- apartment_id=dict(type="str"),
- appliance_id=dict(type="str"),
- host_name=dict(type="str"),
- hardware_type=dict(
- type="str",
- choices=[
- "flash-array-x",
- "flash-array-c",
- "flash-array-x-optane",
- "flash-array-xl",
- ],
- ),
- maintenance_mode=dict(type="bool"),
- unavailable_mode=dict(type="bool"),
- state=dict(type="str", default="present", choices=["present", "absent"]),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- array = get_array(module, fusion)
-
- changed = False
- id = None
- if array is not None:
- id = array.id
-
- if not array and state == "present":
- module.fail_on_missing_params(["hardware_type", "host_name", "appliance_id"])
- changed, id = create_array(module, fusion)
- update_array(
- module, fusion
- ) # update is run to set properties which cannot be set on creation and instead use defaults
- elif array and state == "present":
- changed = update_array(module, fusion)
- elif array and state == "absent":
- changed = changed | delete_array(module, fusion)
- module.exit_json(changed=changed)
-
- if id is not None:
- module.exit_json(changed=changed, id=id)
-
- module.exit_json(changed=changed)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_az.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_az.py
deleted file mode 100644
index b4a493861..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_az.py
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_az
-version_added: '1.0.0'
-short_description: Create Availability Zones in Pure Storage Fusion
-description:
-- Manage an Availability Zone in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the Availability Zone.
- type: str
- required: true
- state:
- description:
- - Define whether the Availability Zone should exist or not.
- default: present
- choices: [ present, absent ]
- type: str
- display_name:
- description:
- - The human name of the Availability Zone.
- - If not provided, defaults to I(name).
- type: str
- region:
- description:
- - Region within which the AZ is created.
- type: str
- required: true
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new AZ foo
- purestorage.fusion.fusion_az:
- name: foo
- display_name: "foo AZ"
- region: region1
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete AZ foo
- purestorage.fusion.fusion_az:
- name: foo
- state: absent
- region: region1
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils import getters
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-
-
-def get_az(module, fusion):
- """Get Availability Zone or None"""
- return getters.get_az(module, fusion, availability_zone_name=module.params["name"])
-
-
-def delete_az(module, fusion):
- """Delete Availability Zone"""
-
- az_api_instance = purefusion.AvailabilityZonesApi(fusion)
-
- changed = True
- if not module.check_mode:
- op = az_api_instance.delete_availability_zone(
- region_name=module.params["region"],
- availability_zone_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- module.exit_json(changed=changed)
-
-
-def create_az(module, fusion):
- """Create Availability Zone"""
-
- az_api_instance = purefusion.AvailabilityZonesApi(fusion)
-
- changed = True
- id = None
- if not module.check_mode:
- if not module.params["display_name"]:
- display_name = module.params["name"]
- else:
- display_name = module.params["display_name"]
-
- azone = purefusion.AvailabilityZonePost(
- name=module.params["name"],
- display_name=display_name,
- )
- op = az_api_instance.create_availability_zone(
- azone, region_name=module.params["region"]
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- module.exit_json(changed=changed, id=id)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- display_name=dict(type="str"),
- region=dict(type="str", required=True),
- state=dict(type="str", default="present", choices=["present", "absent"]),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- azone = get_az(module, fusion)
-
- if not azone and state == "present":
- create_az(module, fusion)
- elif azone and state == "absent":
- delete_az(module, fusion)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_hap.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_hap.py
deleted file mode 100644
index c4df0af49..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_hap.py
+++ /dev/null
@@ -1,314 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_hap
-version_added: '1.0.0'
-short_description: Manage host access policies in Pure Storage Fusion
-description:
-- Create or delete host access policies in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-- Setting passwords is not an idempotent action.
-- Only iSCSI transport is currently supported.
-- iSCSI CHAP is not yet supported.
-options:
- name:
- description:
- - The name of the host access policy.
- type: str
- required: true
- display_name:
- description:
- - The human name of the host access policy.
- type: str
- state:
- description:
- - Define whether the host access policy should exist or not.
- - When removing host access policy all connected volumes must
- have been previously disconnected.
- type: str
- default: present
- choices: [ absent, present ]
- wwns:
- type: list
- elements: str
- description:
- - CURRENTLY NOT SUPPORTED.
- - List of wwns for the host access policy.
- iqn:
- type: str
- description:
- - IQN for the host access policy.
- nqn:
- type: str
- description:
- - CURRENTLY NOT SUPPORTED.
- - NQN for the host access policy.
- personality:
- type: str
- description:
- - Define which operating system the host is.
- default: linux
- choices: ['linux', 'windows', 'hpux', 'vms', 'aix', 'esxi', 'solaris', 'hitachi-vsp', 'oracle-vm-server']
- target_user:
- type: str
- description:
- - CURRENTLY NOT SUPPORTED.
- - Sets the target user name for CHAP authentication.
- - Required with I(target_password).
- - To clear the username/password pair use C(clear) as the password.
- target_password:
- type: str
- description:
- - CURRENTLY NOT SUPPORTED.
- - Sets the target password for CHAP authentication.
- - Password length between 12 and 255 characters.
- - To clear the username/password pair use C(clear) as the password.
- host_user:
- type: str
- description:
- - CURRENTLY NOT SUPPORTED.
- - Sets the host user name for CHAP authentication.
- - Required with I(host_password).
- - To clear the username/password pair use C(clear) as the password.
- host_password:
- type: str
- description:
- - CURRENTLY NOT SUPPORTED.
- - Sets the host password for CHAP authentication.
- - Password length between 12 and 255 characters.
- - To clear the username/password pair use C(clear) as the password.
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new AIX host access policy
- purestorage.fusion.fusion_hap:
- name: foo
- personality: aix
- iqn: "iqn.2005-03.com.RedHat:linux-host1"
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete host access policy
- purestorage.fusion.fusion_hap:
- name: foo
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-import re
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-
-
-def _check_iqn(module, fusion):
- hap_api_instance = purefusion.HostAccessPoliciesApi(fusion)
- hosts = hap_api_instance.list_host_access_policies().items
- for host in hosts:
- if host.iqn == module.params["iqn"] and host.name != module.params["name"]:
- module.fail_json(
- msg="Supplied IQN {0} already used by host access policy {1}".format(
- module.params["iqn"], host.name
- )
- )
-
-
-def get_host(module, fusion):
- """Return host or None"""
- hap_api_instance = purefusion.HostAccessPoliciesApi(fusion)
- try:
- return hap_api_instance.get_host_access_policy(
- host_access_policy_name=module.params["name"]
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def create_hap(module, fusion):
- """Create a new host access policy"""
- hap_api_instance = purefusion.HostAccessPoliciesApi(fusion)
- changed = True
- if not module.check_mode:
- display_name = module.params["display_name"] or module.params["name"]
-
- op = hap_api_instance.create_host_access_policy(
- purefusion.HostAccessPoliciesPost(
- iqn=module.params["iqn"],
- personality=module.params["personality"],
- name=module.params["name"],
- display_name=display_name,
- )
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- module.exit_json(changed=changed, id=id)
-
-
-def delete_hap(module, fusion):
- """Delete a Host Access Policy"""
- hap_api_instance = purefusion.HostAccessPoliciesApi(fusion)
- changed = True
- if not module.check_mode:
- op = hap_api_instance.delete_host_access_policy(
- host_access_policy_name=module.params["name"]
- )
- await_operation(fusion, op)
- module.exit_json(changed=changed)
-
-
-def main():
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- state=dict(type="str", default="present", choices=["absent", "present"]),
- nqn=dict(
- type="str",
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- ),
- iqn=dict(type="str"),
- wwns=dict(
- type="list",
- elements="str",
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- ),
- host_password=dict(
- type="str",
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- no_log=True,
- ),
- host_user=dict(
- type="str",
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- ),
- target_password=dict(
- type="str",
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- no_log=True,
- ),
- target_user=dict(
- type="str",
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- ),
- display_name=dict(type="str"),
- personality=dict(
- type="str",
- default="linux",
- choices=[
- "linux",
- "windows",
- "hpux",
- "vms",
- "aix",
- "esxi",
- "solaris",
- "hitachi-vsp",
- "oracle-vm-server",
- ],
- ),
- )
- )
-
- required_if = [["state", "present", ["personality", "iqn"]]]
-
- module = AnsibleModule(
- argument_spec,
- supports_check_mode=True,
- required_if=required_if,
- )
- fusion = setup_fusion(module)
-
- if module.params["nqn"]:
- module.warn(
- "`nqn` parameter is deprecated and will be removed in version 2.0.0"
- )
- if module.params["wwns"]:
- module.warn(
- "`wwns` parameter is deprecated and will be removed in version 2.0.0"
- )
- if module.params["host_password"]:
- module.warn(
- "`host_password` parameter is deprecated and will be removed in version 2.0.0"
- )
- if module.params["host_user"]:
- module.warn(
- "`host_user` parameter is deprecated and will be removed in version 2.0.0"
- )
- if module.params["target_password"]:
- module.warn(
- "`target_password` parameter is deprecated and will be removed in version 2.0.0"
- )
- if module.params["target_user"]:
- module.warn(
- "`target_user` parameter is deprecated and will be removed in version 2.0.0"
- )
-
- hap_pattern = re.compile("^[a-zA-Z0-9]([a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$")
- iqn_pattern = re.compile(
- r"^iqn\.\d{4}-\d{2}((?<!-)\.(?!-)[a-zA-Z0-9\-]+){1,63}(?<!-)(?<!\.)(:(?!:)[^,\s'\"]+)?$"
- )
-
- if not hap_pattern.match(module.params["name"]):
- module.fail_json(
- msg="Host Access Policy {0} does not conform to naming convention".format(
- module.params["name"]
- )
- )
-
- if module.params["iqn"] is not None and not iqn_pattern.match(module.params["iqn"]):
- module.fail_json(
- msg="IQN {0} is not a valid iSCSI IQN".format(module.params["name"])
- )
-
- state = module.params["state"]
- host = get_host(module, fusion)
- _check_iqn(module, fusion)
-
- if host is None and state == "present":
- create_hap(module, fusion)
- elif host is not None and state == "absent":
- delete_hap(module, fusion)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_hw.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_hw.py
deleted file mode 100644
index 31d313e9d..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_hw.py
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_hw
-version_added: '1.0.0'
-deprecated:
- removed_at_date: "2023-08-09"
- why: Hardware type cannot be modified in Pure Storage Fusion
- alternative: there's no alternative as this functionality has never worked before
-short_description: Create hardware types in Pure Storage Fusion
-description:
-- Create a hardware type in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the hardware type.
- type: str
- state:
- description:
- - Define whether the hardware type should exist or not.
- - Currently there is no mechanism to delete a hardware type.
- default: present
- choices: [ present ]
- type: str
- display_name:
- description:
- - The human name of the hardware type.
- - If not provided, defaults to I(name).
- type: str
- media_type:
- description:
- - Volume size limit in M, G, T or P units.
- type: str
- array_type:
- description:
- - The array type for the hardware type.
- choices: [ FA//X, FA//C ]
- type: str
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-# this module does nothing, thus no example is provided
-EXAMPLES = r"""
-"""
-
-RETURN = r"""
-"""
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str"),
- display_name=dict(type="str"),
- array_type=dict(type="str", choices=["FA//X", "FA//C"]),
- media_type=dict(type="str"),
- state=dict(type="str", default="present", choices=["present"]),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_info.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_info.py
deleted file mode 100644
index be019d3d2..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_info.py
+++ /dev/null
@@ -1,1130 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2021, Simon Dodsley (simon@purestorage.com), Andrej Pajtas (apajtas@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_info
-version_added: '1.0.0'
-short_description: Collect information from Pure Fusion
-description:
- - Collect information from a Pure Fusion environment.
- - By default, the module will collect basic
- information including counts for arrays, availability_zones, volumes, snapshots
- . Fleet capacity and data reduction rates are also provided.
- - Additional information can be collected based on the configured set of arguments.
-author:
- - Pure Storage ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- gather_subset:
- description:
- - When supplied, this argument will define the information to be collected.
- Possible values for this include all, minimum, roles, users, arrays, hardware_types,
- volumes, host_access_policies, storage_classes, protection_policies, placement_groups,
- network_interfaces, availability_zones, network_interface_groups, storage_endpoints,
- snapshots, regions, storage_services, tenants, tenant_spaces, network_interface_groups and api_clients.
- type: list
- elements: str
- required: false
- default: minimum
-extends_documentation_fragment:
- - purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Collect default set of information
- purestorage.fusion.fusion_info:
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
- register: fusion_info
-
-- name: Show default information
- ansible.builtin.debug:
- msg: "{{ fusion_info['fusion_info']['default'] }}"
-
-- name: Collect all information
- purestorage.fusion.fusion_info:
- gather_subset:
- - all
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Show all information
- ansible.builtin.debug:
- msg: "{{ fusion_info['fusion_info'] }}"
-"""
-
-RETURN = r"""
-fusion_info:
- description: Returns the information collected from Fusion
- returned: always
- type: dict
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-import time
-import http
-
-
-def _convert_microseconds(micros):
- seconds = (micros / 1000) % 60
- minutes = (micros / (1000 * 60)) % 60
- hours = (micros / (1000 * 60 * 60)) % 24
- return seconds, minutes, hours
-
-
-def _api_permission_denied_handler(name):
- """Return decorator which catches #403 errors"""
-
- def inner(func):
- def wrapper(module, fusion, *args, **kwargs):
- try:
- return func(module, fusion, *args, **kwargs)
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- module.warn(f"Cannot get [{name} dict], reason: Permission denied")
- return None
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- return wrapper
-
- return inner
-
-
-def generate_default_dict(module, fusion):
- def warning_api_exception(name):
- module.warn(f"Cannot get {name} in [default dict], reason: Permission denied")
-
- def warning_argument_none(name, requirement):
- module.warn(
- f"Cannot get {name} in [default dict], reason: Required argument `{requirement}` not available."
- )
-
- # All values are independent on each other - if getting one value fails, we will show warning and continue.
- # That's also the reason why there's so many nested for loops repeating all over again.
- version = None
- users_num = None
- protection_policies_num = None
- host_access_policies_num = None
- hardware_types_num = None
- storage_services = None
- storage_services_num = None
- tenants = None
- tenants_num = None
- regions = None
- regions_num = None
- roles = None
- roles_num = None
- storage_classes_num = None
- role_assignments_num = None
- tenant_spaces_num = None
- volumes_num = None
- placement_groups_num = None
- snapshots_num = None
- availability_zones_num = None
- arrays_num = None
- network_interfaces_num = None
- network_interface_groups_num = None
- storage_endpoints_num = None
-
- try:
- version = purefusion.DefaultApi(fusion).get_version().version
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("API version")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- users_num = len(purefusion.IdentityManagerApi(fusion).list_users())
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Users")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- protection_policies_num = len(
- purefusion.ProtectionPoliciesApi(fusion).list_protection_policies().items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Protection Policies")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- host_access_policies_num = len(
- purefusion.HostAccessPoliciesApi(fusion).list_host_access_policies().items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Host Access Policies")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- hardware_types_num = len(
- purefusion.HardwareTypesApi(fusion).list_hardware_types().items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Hardware Types")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- storage_services = purefusion.StorageServicesApi(fusion).list_storage_services()
- storage_services_num = len(storage_services.items)
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Storage Services")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- tenants = purefusion.TenantsApi(fusion).list_tenants()
- tenants_num = len(tenants.items)
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Tenants")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- regions = purefusion.RegionsApi(fusion).list_regions()
- regions_num = len(regions.items)
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Regions")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- roles = purefusion.RolesApi(fusion).list_roles()
- roles_num = len(roles)
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Roles")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- if storage_services is not None:
- try:
- storage_class_api_instance = purefusion.StorageClassesApi(fusion)
- storage_classes_num = sum(
- len(
- storage_class_api_instance.list_storage_classes(
- storage_service_name=storage_service.name
- ).items
- )
- for storage_service in storage_services.items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Storage Classes")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
- else:
- warning_argument_none("Storage Classes", "storage_services")
-
- if roles is not None:
- try:
- role_assign_api_instance = purefusion.RoleAssignmentsApi(fusion)
- role_assignments_num = sum(
- len(role_assign_api_instance.list_role_assignments(role_name=role.name))
- for role in roles
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Role Assignments")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
- else:
- warning_argument_none("Role Assignments", "roles")
-
- if tenants is not None:
- tenantspace_api_instance = purefusion.TenantSpacesApi(fusion)
-
- try:
- tenant_spaces_num = sum(
- len(
- tenantspace_api_instance.list_tenant_spaces(
- tenant_name=tenant.name
- ).items
- )
- for tenant in tenants.items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Tenant Spaces")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- vol_api_instance = purefusion.VolumesApi(fusion)
- volumes_num = sum(
- len(
- vol_api_instance.list_volumes(
- tenant_name=tenant.name,
- tenant_space_name=tenant_space.name,
- ).items
- )
- for tenant in tenants.items
- for tenant_space in tenantspace_api_instance.list_tenant_spaces(
- tenant_name=tenant.name
- ).items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Volumes")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- plgrp_api_instance = purefusion.PlacementGroupsApi(fusion)
- placement_groups_num = sum(
- len(
- plgrp_api_instance.list_placement_groups(
- tenant_name=tenant.name,
- tenant_space_name=tenant_space.name,
- ).items
- )
- for tenant in tenants.items
- for tenant_space in tenantspace_api_instance.list_tenant_spaces(
- tenant_name=tenant.name
- ).items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Placement Groups")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- snapshot_api_instance = purefusion.SnapshotsApi(fusion)
- snapshots_num = sum(
- len(
- snapshot_api_instance.list_snapshots(
- tenant_name=tenant.name,
- tenant_space_name=tenant_space.name,
- ).items
- )
- for tenant in tenants.items
- for tenant_space in tenantspace_api_instance.list_tenant_spaces(
- tenant_name=tenant.name
- ).items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Snapshots")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
- else:
- warning_argument_none("Tenant Spaces", "tenants")
- warning_argument_none("Volumes", "tenants")
- warning_argument_none("Placement Groups", "tenants")
- warning_argument_none("Snapshots", "tenants")
-
- if regions is not None:
- az_api_instance = purefusion.AvailabilityZonesApi(fusion)
-
- try:
- availability_zones_num = sum(
- len(
- az_api_instance.list_availability_zones(
- region_name=region.name
- ).items
- )
- for region in regions.items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Availability Zones")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- arrays_api_instance = purefusion.ArraysApi(fusion)
- arrays_num = sum(
- len(
- arrays_api_instance.list_arrays(
- availability_zone_name=availability_zone.name,
- region_name=region.name,
- ).items
- )
- for region in regions.items
- for availability_zone in az_api_instance.list_availability_zones(
- region_name=region.name
- ).items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Arrays")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- nig_api_instance = purefusion.NetworkInterfaceGroupsApi(fusion)
- network_interface_groups_num = sum(
- len(
- nig_api_instance.list_network_interface_groups(
- availability_zone_name=availability_zone.name,
- region_name=region.name,
- ).items
- )
- for region in regions.items
- for availability_zone in az_api_instance.list_availability_zones(
- region_name=region.name
- ).items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Network Interface Groups")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- send_api_instance = purefusion.StorageEndpointsApi(fusion)
- storage_endpoints_num = sum(
- len(
- send_api_instance.list_storage_endpoints(
- availability_zone_name=availability_zone.name,
- region_name=region.name,
- ).items
- )
- for region in regions.items
- for availability_zone in az_api_instance.list_availability_zones(
- region_name=region.name
- ).items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Storage Endpoints")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
-
- try:
- nic_api_instance = purefusion.NetworkInterfacesApi(fusion)
- network_interfaces_num = sum(
- len(
- nic_api_instance.list_network_interfaces(
- availability_zone_name=availability_zone.name,
- region_name=region.name,
- array_name=array_detail.name,
- ).items
- )
- for region in regions.items
- for availability_zone in az_api_instance.list_availability_zones(
- region_name=region.name
- ).items
- for array_detail in arrays_api_instance.list_arrays(
- availability_zone_name=availability_zone.name,
- region_name=region.name,
- ).items
- )
- except purefusion.rest.ApiException as exc:
- if exc.status == http.HTTPStatus.FORBIDDEN:
- warning_api_exception("Network Interfaces")
- else:
- # other exceptions will be handled by our exception hook
- raise exc
- else:
- warning_argument_none("Availability Zones", "regions")
- warning_argument_none("Network Interfaces", "regions")
- warning_argument_none("Network Interface Groups", "regions")
- warning_argument_none("Storage Endpoints", "regions")
- warning_argument_none("Arrays", "regions")
-
- return {
- "version": version,
- "users": users_num,
- "protection_policies": protection_policies_num,
- "host_access_policies": host_access_policies_num,
- "hardware_types": hardware_types_num,
- "storage_services": storage_services_num,
- "tenants": tenants_num,
- "regions": regions_num,
- "storage_classes": storage_classes_num,
- "roles": roles_num,
- "role_assignments": role_assignments_num,
- "tenant_spaces": tenant_spaces_num,
- "volumes": volumes_num,
- "placement_groups": placement_groups_num,
- "snapshots": snapshots_num,
- "availability_zones": availability_zones_num,
- "arrays": arrays_num,
- "network_interfaces": network_interfaces_num,
- "network_interface_groups": network_interface_groups_num,
- "storage_endpoints": storage_endpoints_num,
- }
-
-
-@_api_permission_denied_handler("network_interfaces")
-def generate_nics_dict(module, fusion):
- nics_info = {}
- nic_api_instance = purefusion.NetworkInterfacesApi(fusion)
- arrays_api_instance = purefusion.ArraysApi(fusion)
- az_api_instance = purefusion.AvailabilityZonesApi(fusion)
- regions_api_instance = purefusion.RegionsApi(fusion)
- regions = regions_api_instance.list_regions()
- for region in regions.items:
- azs = az_api_instance.list_availability_zones(region_name=region.name)
- for az in azs.items:
- array_details = arrays_api_instance.list_arrays(
- availability_zone_name=az.name,
- region_name=region.name,
- )
- for array_detail in array_details.items:
- array_name = az.name + "/" + array_detail.name
- nics_info[array_name] = {}
- nics = nic_api_instance.list_network_interfaces(
- availability_zone_name=az.name,
- region_name=region.name,
- array_name=array_detail.name,
- )
-
- for nic in nics.items:
- nics_info[array_name][nic.name] = {
- "enabled": nic.enabled,
- "display_name": nic.display_name,
- "interface_type": nic.interface_type,
- "services": nic.services,
- "max_speed": nic.max_speed,
- "vlan": nic.eth.vlan,
- "address": nic.eth.address,
- "mac_address": nic.eth.mac_address,
- "gateway": nic.eth.gateway,
- "mtu": nic.eth.mtu,
- "network_interface_group": nic.network_interface_group.name,
- "availability_zone": nic.availability_zone.name,
- }
- return nics_info
-
-
-@_api_permission_denied_handler("host_access_policies")
-def generate_hap_dict(module, fusion):
- hap_info = {}
- api_instance = purefusion.HostAccessPoliciesApi(fusion)
- hosts = api_instance.list_host_access_policies()
- for host in hosts.items:
- name = host.name
- hap_info[name] = {
- "personality": host.personality,
- "display_name": host.display_name,
- "iqn": host.iqn,
- }
- return hap_info
-
-
-@_api_permission_denied_handler("arrays")
-def generate_array_dict(module, fusion):
- array_info = {}
- array_api_instance = purefusion.ArraysApi(fusion)
- az_api_instance = purefusion.AvailabilityZonesApi(fusion)
- regions_api_instance = purefusion.RegionsApi(fusion)
- regions = regions_api_instance.list_regions()
- for region in regions.items:
- azs = az_api_instance.list_availability_zones(region_name=region.name)
- for az in azs.items:
- arrays = array_api_instance.list_arrays(
- availability_zone_name=az.name,
- region_name=region.name,
- )
- for array in arrays.items:
- array_name = array.name
- array_space = array_api_instance.get_array_space(
- availability_zone_name=az.name,
- array_name=array_name,
- region_name=region.name,
- )
- array_perf = array_api_instance.get_array_performance(
- availability_zone_name=az.name,
- array_name=array_name,
- region_name=region.name,
- )
- array_info[array_name] = {
- "region": region.name,
- "availability_zone": az.name,
- "host_name": array.host_name,
- "maintenance_mode": array.maintenance_mode,
- "unavailable_mode": array.unavailable_mode,
- "display_name": array.display_name,
- "hardware_type": array.hardware_type.name,
- "appliance_id": array.appliance_id,
- "apartment_id": getattr(array, "apartment_id", None),
- "space": {
- "total_physical_space": array_space.total_physical_space,
- },
- "performance": {
- "read_bandwidth": array_perf.read_bandwidth,
- "read_latency_us": array_perf.read_latency_us,
- "reads_per_sec": array_perf.reads_per_sec,
- "write_bandwidth": array_perf.write_bandwidth,
- "write_latency_us": array_perf.write_latency_us,
- "writes_per_sec": array_perf.writes_per_sec,
- },
- }
- return array_info
-
-
-@_api_permission_denied_handler("placement_groups")
-def generate_pg_dict(module, fusion):
- pg_info = {}
- tenant_api_instance = purefusion.TenantsApi(fusion)
- tenantspace_api_instance = purefusion.TenantSpacesApi(fusion)
- pg_api_instance = purefusion.PlacementGroupsApi(fusion)
- tenants = tenant_api_instance.list_tenants()
- for tenant in tenants.items:
- tenant_spaces = tenantspace_api_instance.list_tenant_spaces(
- tenant_name=tenant.name
- ).items
- for tenant_space in tenant_spaces:
- groups = pg_api_instance.list_placement_groups(
- tenant_name=tenant.name,
- tenant_space_name=tenant_space.name,
- )
- for group in groups.items:
- group_name = tenant.name + "/" + tenant_space.name + "/" + group.name
- pg_info[group_name] = {
- "tenant": group.tenant.name,
- "display_name": group.display_name,
- "placement_engine": group.placement_engine,
- "tenant_space": group.tenant_space.name,
- "az": group.availability_zone.name,
- "array": getattr(group.array, "name", None),
- }
- return pg_info
-
-
-@_api_permission_denied_handler("tenant_spaces")
-def generate_ts_dict(module, fusion):
- ts_info = {}
- tenant_api_instance = purefusion.TenantsApi(fusion)
- tenantspace_api_instance = purefusion.TenantSpacesApi(fusion)
- tenants = tenant_api_instance.list_tenants()
- for tenant in tenants.items:
- tenant_spaces = tenantspace_api_instance.list_tenant_spaces(
- tenant_name=tenant.name
- ).items
- for tenant_space in tenant_spaces:
- ts_name = tenant.name + "/" + tenant_space.name
- ts_info[ts_name] = {
- "tenant": tenant.name,
- "display_name": tenant_space.display_name,
- }
- return ts_info
-
-
-@_api_permission_denied_handler("protection_policies")
-def generate_pp_dict(module, fusion):
- pp_info = {}
- api_instance = purefusion.ProtectionPoliciesApi(fusion)
- policies = api_instance.list_protection_policies()
- for policy in policies.items:
- policy_name = policy.name
- pp_info[policy_name] = {
- "objectives": policy.objectives,
- }
- return pp_info
-
-
-@_api_permission_denied_handler("tenants")
-def generate_tenant_dict(module, fusion):
- tenants_api_instance = purefusion.TenantsApi(fusion)
- return {
- tenant.name: {
- "display_name": tenant.display_name,
- }
- for tenant in tenants_api_instance.list_tenants().items
- }
-
-
-@_api_permission_denied_handler("regions")
-def generate_regions_dict(module, fusion):
- regions_api_instance = purefusion.RegionsApi(fusion)
- return {
- region.name: {
- "display_name": region.display_name,
- }
- for region in regions_api_instance.list_regions().items
- }
-
-
-@_api_permission_denied_handler("availability_zones")
-def generate_zones_dict(module, fusion):
- zones_info = {}
- az_api_instance = purefusion.AvailabilityZonesApi(fusion)
- regions_api_instance = purefusion.RegionsApi(fusion)
- regions = regions_api_instance.list_regions()
- for region in regions.items:
- zones = az_api_instance.list_availability_zones(region_name=region.name)
- for zone in zones.items:
- az_name = zone.name
- zones_info[az_name] = {
- "display_name": zone.display_name,
- "region": zone.region.name,
- }
- return zones_info
-
-
-@_api_permission_denied_handler("role_assignments")
-def generate_ras_dict(module, fusion):
- ras_info = {}
- ras_api_instance = purefusion.RoleAssignmentsApi(fusion)
- role_api_instance = purefusion.RolesApi(fusion)
- roles = role_api_instance.list_roles()
- for role in roles:
- ras = ras_api_instance.list_role_assignments(role_name=role.name)
- for assignment in ras:
- name = assignment.name
- ras_info[name] = {
- "display_name": assignment.display_name,
- "role": assignment.role.name,
- "scope": assignment.scope.name,
- }
- return ras_info
-
-
-@_api_permission_denied_handler("roles")
-def generate_roles_dict(module, fusion):
- roles_info = {}
- api_instance = purefusion.RolesApi(fusion)
- roles = api_instance.list_roles()
- for role in roles:
- name = role.name
- roles_info[name] = {
- "display_name": role.display_name,
- "scopes": role.assignable_scopes,
- }
- return roles_info
-
-
-@_api_permission_denied_handler("api_clients")
-def generate_api_client_dict(module, fusion):
- client_info = {}
- api_instance = purefusion.IdentityManagerApi(fusion)
- clients = api_instance.list_api_clients()
- for client in clients:
- client_info[client.name] = {
- "display_name": client.display_name,
- "issuer": client.issuer,
- "public_key": client.public_key,
- "creator_id": client.creator_id,
- "last_key_update": time.strftime(
- "%a, %d %b %Y %H:%M:%S %Z",
- time.localtime(client.last_key_update / 1000),
- ),
- "last_used": time.strftime(
- "%a, %d %b %Y %H:%M:%S %Z",
- time.localtime(client.last_used / 1000),
- ),
- }
- return client_info
-
-
-@_api_permission_denied_handler("users")
-def generate_users_dict(module, fusion):
- users_info = {}
- api_instance = purefusion.IdentityManagerApi(fusion)
- users = api_instance.list_users()
- for user in users:
- users_info[user.name] = {
- "display_name": user.display_name,
- "email": user.email,
- "id": user.id,
- }
- return users_info
-
-
-@_api_permission_denied_handler("hardware_types")
-def generate_hardware_types_dict(module, fusion):
- hardware_info = {}
- api_instance = purefusion.HardwareTypesApi(fusion)
- hw_types = api_instance.list_hardware_types()
- for hw_type in hw_types.items:
- hardware_info[hw_type.name] = {
- "array_type": hw_type.array_type,
- "display_name": hw_type.display_name,
- "media_type": hw_type.media_type,
- }
- return hardware_info
-
-
-@_api_permission_denied_handler("storage_classes")
-def generate_sc_dict(module, fusion):
- sc_info = {}
- ss_api_instance = purefusion.StorageServicesApi(fusion)
- sc_api_instance = purefusion.StorageClassesApi(fusion)
- services = ss_api_instance.list_storage_services()
- for service in services.items:
- classes = sc_api_instance.list_storage_classes(
- storage_service_name=service.name,
- )
- for s_class in classes.items:
- sc_info[s_class.name] = {
- "bandwidth_limit": getattr(s_class, "bandwidth_limit", None),
- "iops_limit": getattr(s_class, "iops_limit", None),
- "size_limit": getattr(s_class, "size_limit", None),
- "display_name": s_class.display_name,
- "storage_service": service.name,
- }
- return sc_info
-
-
-@_api_permission_denied_handler("storage_services")
-def generate_storserv_dict(module, fusion):
- ss_dict = {}
- ss_api_instance = purefusion.StorageServicesApi(fusion)
- services = ss_api_instance.list_storage_services()
- for service in services.items:
- ss_dict[service.name] = {
- "display_name": service.display_name,
- "hardware_types": None,
- }
- # can be None if we don't have permission to see this
- if service.hardware_types is not None:
- ss_dict[service.name]["hardware_types"] = []
- for hwtype in service.hardware_types:
- ss_dict[service.name]["hardware_types"].append(hwtype.name)
- return ss_dict
-
-
-@_api_permission_denied_handler("storage_endpoints")
-def generate_se_dict(module, fusion):
- se_dict = {}
- se_api_instance = purefusion.StorageEndpointsApi(fusion)
- az_api_instance = purefusion.AvailabilityZonesApi(fusion)
- regions_api_instance = purefusion.RegionsApi(fusion)
- regions = regions_api_instance.list_regions()
- for region in regions.items:
- azs = az_api_instance.list_availability_zones(region_name=region.name)
- for az in azs.items:
- endpoints = se_api_instance.list_storage_endpoints(
- region_name=region.name,
- availability_zone_name=az.name,
- )
- for endpoint in endpoints.items:
- name = region.name + "/" + az.name + "/" + endpoint.name
- se_dict[name] = {
- "display_name": endpoint.display_name,
- "endpoint_type": endpoint.endpoint_type,
- "iscsi_interfaces": [],
- }
- for iface in endpoint.iscsi.discovery_interfaces:
- dct = {
- "address": iface.address,
- "gateway": iface.gateway,
- "mtu": iface.mtu,
- "network_interface_groups": None,
- }
- if iface.network_interface_groups is not None:
- dct["network_interface_groups"] = [
- nig.name for nig in iface.network_interface_groups
- ]
- se_dict[name]["iscsi_interfaces"].append(dct)
- return se_dict
-
-
-@_api_permission_denied_handler("network_interface_groups")
-def generate_nigs_dict(module, fusion):
- nigs_dict = {}
- nig_api_instance = purefusion.NetworkInterfaceGroupsApi(fusion)
- az_api_instance = purefusion.AvailabilityZonesApi(fusion)
- regions_api_instance = purefusion.RegionsApi(fusion)
- regions = regions_api_instance.list_regions()
- for region in regions.items:
- azs = az_api_instance.list_availability_zones(region_name=region.name)
- for az in azs.items:
- nigs = nig_api_instance.list_network_interface_groups(
- region_name=region.name,
- availability_zone_name=az.name,
- )
- for nig in nigs.items:
- name = region.name + "/" + az.name + "/" + nig.name
- nigs_dict[name] = {
- "display_name": nig.display_name,
- "gateway": nig.eth.gateway,
- "prefix": nig.eth.prefix,
- "mtu": nig.eth.mtu,
- }
- return nigs_dict
-
-
-@_api_permission_denied_handler("snapshots")
-def generate_snap_dicts(module, fusion):
- snap_dict = {}
- vsnap_dict = {}
- tenant_api_instance = purefusion.TenantsApi(fusion)
- tenantspace_api_instance = purefusion.TenantSpacesApi(fusion)
- snap_api_instance = purefusion.SnapshotsApi(fusion)
- vsnap_api_instance = purefusion.VolumeSnapshotsApi(fusion)
- tenants = tenant_api_instance.list_tenants()
- for tenant in tenants.items:
- tenant_spaces = tenantspace_api_instance.list_tenant_spaces(
- tenant_name=tenant.name
- ).items
- for tenant_space in tenant_spaces:
- snaps = snap_api_instance.list_snapshots(
- tenant_name=tenant.name,
- tenant_space_name=tenant_space.name,
- )
- for snap in snaps.items:
- snap_name = tenant.name + "/" + tenant_space.name + "/" + snap.name
- secs, mins, hours = _convert_microseconds(snap.time_remaining)
- snap_dict[snap_name] = {
- "display_name": snap.display_name,
- "protection_policy": snap.protection_policy,
- "time_remaining": "{0} hours, {1} mins, {2} secs".format(
- int(hours), int(mins), int(secs)
- ),
- "volume_snapshots_link": snap.volume_snapshots_link,
- }
- vsnaps = vsnap_api_instance.list_volume_snapshots(
- tenant_name=tenant.name,
- tenant_space_name=tenant_space.name,
- snapshot_name=snap.name,
- )
- for vsnap in vsnaps.items:
- vsnap_name = (
- tenant.name
- + "/"
- + tenant_space.name
- + "/"
- + snap.name
- + "/"
- + vsnap.name
- )
- secs, mins, hours = _convert_microseconds(vsnap.time_remaining)
- vsnap_dict[vsnap_name] = {
- "size": vsnap.size,
- "display_name": vsnap.display_name,
- "protection_policy": vsnap.protection_policy,
- "serial_number": vsnap.serial_number,
- "created_at": time.strftime(
- "%a, %d %b %Y %H:%M:%S %Z",
- time.localtime(vsnap.created_at / 1000),
- ),
- "time_remaining": "{0} hours, {1} mins, {2} secs".format(
- int(hours), int(mins), int(secs)
- ),
- "placement_group": vsnap.placement_group.name,
- }
- return snap_dict, vsnap_dict
-
-
-@_api_permission_denied_handler("volumes")
-def generate_volumes_dict(module, fusion):
- volume_info = {}
-
- tenant_api_instance = purefusion.TenantsApi(fusion)
- vol_api_instance = purefusion.VolumesApi(fusion)
- tenant_space_api_instance = purefusion.TenantSpacesApi(fusion)
-
- tenants = tenant_api_instance.list_tenants()
- for tenant in tenants.items:
- tenant_spaces = tenant_space_api_instance.list_tenant_spaces(
- tenant_name=tenant.name
- ).items
- for tenant_space in tenant_spaces:
- volumes = vol_api_instance.list_volumes(
- tenant_name=tenant.name,
- tenant_space_name=tenant_space.name,
- )
- for volume in volumes.items:
- vol_name = tenant.name + "/" + tenant_space.name + "/" + volume.name
- volume_info[vol_name] = {
- "tenant": tenant.name,
- "tenant_space": tenant_space.name,
- "name": volume.name,
- "size": volume.size,
- "display_name": volume.display_name,
- "placement_group": volume.placement_group.name,
- "source_volume_snapshot": getattr(
- volume.source_volume_snapshot, "name", None
- ),
- "protection_policy": getattr(
- volume.protection_policy, "name", None
- ),
- "storage_class": volume.storage_class.name,
- "serial_number": volume.serial_number,
- "target": {},
- "array": getattr(volume.array, "name", None),
- }
-
- volume_info[vol_name]["target"] = {
- "iscsi": {
- "addresses": volume.target.iscsi.addresses,
- "iqn": volume.target.iscsi.iqn,
- },
- "nvme": {
- "addresses": None,
- "nqn": None,
- },
- "fc": {
- "addresses": None,
- "wwns": None,
- },
- }
- return volume_info
-
-
-def main():
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(gather_subset=dict(default="minimum", type="list", elements="str"))
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
-
- # will handle all errors (except #403 which should be handled in code)
- fusion = setup_fusion(module)
-
- subset = [test.lower() for test in module.params["gather_subset"]]
- valid_subsets = (
- "all",
- "minimum",
- "roles",
- "users",
- "placements",
- "arrays",
- "hardware_types",
- "volumes",
- "hosts",
- "storage_classes",
- "protection_policies",
- "placement_groups",
- "interfaces",
- "zones",
- "nigs",
- "storage_endpoints",
- "snapshots",
- "storage_services",
- "tenants",
- "tenant_spaces",
- "network_interface_groups",
- "api_clients",
- "availability_zones",
- "host_access_policies",
- "network_interfaces",
- "regions",
- )
- for option in subset:
- if option not in valid_subsets:
- module.fail_json(
- msg=f"value gather_subset must be one or more of: {','.join(valid_subsets)}, got: {','.join(subset)}\nvalue {option} is not allowed"
- )
-
- info = {}
-
- if "minimum" in subset or "all" in subset:
- info["default"] = generate_default_dict(module, fusion)
- if "hardware_types" in subset or "all" in subset:
- info["hardware_types"] = generate_hardware_types_dict(module, fusion)
- if "users" in subset or "all" in subset:
- info["users"] = generate_users_dict(module, fusion)
- if "regions" in subset or "all" in subset:
- info["regions"] = generate_regions_dict(module, fusion)
- if "availability_zones" in subset or "all" in subset or "zones" in subset:
- info["availability_zones"] = generate_zones_dict(module, fusion)
- if "zones" in subset:
- module.warn(
- "The 'zones' subset is deprecated and will be removed in the version 2.0.0\nUse 'availability_zones' subset instead."
- )
- if "roles" in subset or "all" in subset:
- info["roles"] = generate_roles_dict(module, fusion)
- info["role_assignments"] = generate_ras_dict(module, fusion)
- if "storage_services" in subset or "all" in subset:
- info["storage_services"] = generate_storserv_dict(module, fusion)
- if "volumes" in subset or "all" in subset:
- info["volumes"] = generate_volumes_dict(module, fusion)
- if "protection_policies" in subset or "all" in subset:
- info["protection_policies"] = generate_pp_dict(module, fusion)
- if "placement_groups" in subset or "all" in subset or "placements" in subset:
- info["placement_groups"] = generate_pg_dict(module, fusion)
- if "placements" in subset:
- module.warn(
- "The 'placements' subset is deprecated and will be removed in the version 1.7.0"
- )
- if "storage_classes" in subset or "all" in subset:
- info["storage_classes"] = generate_sc_dict(module, fusion)
- if "network_interfaces" in subset or "all" in subset or "interfaces" in subset:
- info["network_interfaces"] = generate_nics_dict(module, fusion)
- if "interfaces" in subset:
- module.warn(
- "The 'interfaces' subset is deprecated and will be removed in the version 2.0.0\nUse 'network_interfaces' subset instead."
- )
- if "host_access_policies" in subset or "all" in subset or "hosts" in subset:
- info["host_access_policies"] = generate_hap_dict(module, fusion)
- if "hosts" in subset:
- module.warn(
- "The 'hosts' subset is deprecated and will be removed in the version 2.0.0\nUse 'host_access_policies' subset instead."
- )
- if "arrays" in subset or "all" in subset:
- info["arrays"] = generate_array_dict(module, fusion)
- if "tenants" in subset or "all" in subset:
- info["tenants"] = generate_tenant_dict(module, fusion)
- if "tenant_spaces" in subset or "all" in subset:
- info["tenant_spaces"] = generate_ts_dict(module, fusion)
- if "storage_endpoints" in subset or "all" in subset:
- info["storage_endpoints"] = generate_se_dict(module, fusion)
- if "api_clients" in subset or "all" in subset:
- info["api_clients"] = generate_api_client_dict(module, fusion)
- if "network_interface_groups" in subset or "all" in subset or "nigs" in subset:
- info["network_interface_groups"] = generate_nigs_dict(module, fusion)
- if "nigs" in subset:
- module.warn(
- "The 'nigs' subset is deprecated and will be removed in the version 1.7.0"
- )
- if "snapshots" in subset or "all" in subset:
- snap_dicts = generate_snap_dicts(module, fusion)
- if snap_dicts is not None:
- info["snapshots"], info["volume_snapshots"] = snap_dicts
- else:
- info["snapshots"], info["volume_snapshots"] = None, None
-
- module.exit_json(changed=False, fusion_info=info)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ni.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ni.py
deleted file mode 100644
index 82c896fac..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ni.py
+++ /dev/null
@@ -1,245 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2023, Andrej Pajtas (apajtas@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_ni
-version_added: '1.0.0'
-short_description: Manage network interfaces in Pure Storage Fusion
-description:
-- Update parameters of network interfaces in Pure Storage Fusion.
-notes:
-- Supports C(check_mode).
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-options:
- name:
- description:
- - The name of the network interface.
- type: str
- required: true
- display_name:
- description:
- - The human name of the network interface.
- - If not provided, defaults to I(name).
- type: str
- region:
- description:
- - The name of the region the availability zone is in.
- type: str
- required: true
- availability_zone:
- aliases: [ az ]
- description:
- - The name of the availability zone for the network interface.
- type: str
- required: true
- array:
- description:
- - The name of the array the network interface belongs to.
- type: str
- required: true
- eth:
- description:
- - The IP address associated with the network interface.
- - IP address must include a CIDR notation.
- - Only IPv4 is supported at the moment.
- - Required together with `network_interface_group` parameter.
- type: str
- enabled:
- description:
- - True if network interface is in use.
- type: bool
- network_interface_group:
- description:
- - The name of the network interface group this network interface belongs to.
- type: str
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Patch network interface
- purestorage.fusion.fusion_ni:
- name: foo
- region: us-west
- availability_zone: bar
- array: array0
- eth: 10.21.200.124/24
- enabled: true
- network_interface_group: subnet-0
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.getters import (
- get_array,
- get_az,
- get_region,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.networking import (
- is_valid_network,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-
-
-def get_ni(module, fusion):
- """Get Network Interface or None"""
- ni_api_instance = purefusion.NetworkInterfacesApi(fusion)
- try:
- return ni_api_instance.get_network_interface(
- region_name=module.params["region"],
- availability_zone_name=module.params["availability_zone"],
- array_name=module.params["array"],
- net_intf_name=module.params["name"],
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def update_ni(module, fusion, ni):
- """Update Network Interface"""
- ni_api_instance = purefusion.NetworkInterfacesApi(fusion)
-
- patches = []
- if (
- module.params["display_name"]
- and module.params["display_name"] != ni.display_name
- ):
- patch = purefusion.NetworkInterfacePatch(
- display_name=purefusion.NullableString(module.params["display_name"]),
- )
- patches.append(patch)
-
- if module.params["enabled"] is not None and module.params["enabled"] != ni.enabled:
- patch = purefusion.NetworkInterfacePatch(
- enabled=purefusion.NullableBoolean(module.params["enabled"]),
- )
- patches.append(patch)
-
- if (
- module.params["network_interface_group"]
- and module.params["network_interface_group"] != ni.network_interface_group
- ):
- if module.params["eth"] and module.params["eth"] != ni.eth:
- patch = purefusion.NetworkInterfacePatch(
- eth=purefusion.NetworkInterfacePatchEth(
- purefusion.NullableString(module.params["eth"])
- ),
- network_interface_group=purefusion.NullableString(
- module.params["network_interface_group"]
- ),
- )
- else:
- patch = purefusion.NetworkInterfacePatch(
- network_interface_group=purefusion.NullableString(
- module.params["network_interface_group"]
- ),
- )
- patches.append(patch)
- id = None
- if not module.check_mode:
- for patch in patches:
- op = ni_api_instance.update_network_interface(
- patch,
- region_name=module.params["region"],
- availability_zone_name=module.params["availability_zone"],
- array_name=module.params["array"],
- net_intf_name=module.params["name"],
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- changed = len(patches) != 0
-
- module.exit_json(changed=changed, id=id)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- display_name=dict(type="str"),
- region=dict(type="str", required=True),
- availability_zone=dict(type="str", required=True, aliases=["az"]),
- array=dict(type="str", required=True),
- eth=dict(type="str"),
- enabled=dict(type="bool"),
- network_interface_group=dict(type="str"),
- )
- )
-
- required_by = {
- "eth": "network_interface_group",
- }
-
- module = AnsibleModule(
- argument_spec,
- supports_check_mode=True,
- required_by=required_by,
- )
-
- fusion = setup_fusion(module)
-
- if module.params["eth"] and not is_valid_network(module.params["eth"]):
- module.fail_json(
- msg="`eth` '{0}' is not a valid address in CIDR notation".format(
- module.params["eth"]
- )
- )
-
- if not get_region(module, fusion):
- module.fail_json(
- msg="Region {0} does not exist.".format(module.params["region"])
- )
-
- if not get_az(module, fusion):
- module.fail_json(
- msg="Availability Zone {0} does not exist.".format(
- module.params["availability_zone"]
- )
- )
-
- if not get_array(module, fusion):
- module.fail_json(msg="Array {0} does not exist.".format(module.params["array"]))
-
- ni = get_ni(module, fusion)
- if not ni:
- module.fail_json(
- msg="Network Interface {0} does not exist".format(module.params["name"])
- )
-
- update_ni(module, fusion, ni)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_nig.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_nig.py
deleted file mode 100644
index d40b813b9..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_nig.py
+++ /dev/null
@@ -1,276 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_nig
-version_added: '1.0.0'
-short_description: Manage Network Interface Groups in Pure Storage Fusion
-description:
-- Create, delete and modify network interface groups in Pure Storage Fusion.
-- Currently this only supports a single tenant subnet per tenant network
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the network interface group.
- type: str
- required: true
- display_name:
- description:
- - The human name of the network interface group.
- - If not provided, defaults to I(name).
- type: str
- state:
- description:
- - Define whether the network interface group should exist or not.
- type: str
- default: present
- choices: [ absent, present ]
- availability_zone:
- aliases: [ az ]
- description:
- - The name of the availability zone for the network interface group.
- type: str
- required: true
- region:
- description:
- - Region for the network interface group.
- type: str
- required: true
- gateway:
- description:
- - "Address of the subnet gateway.
- Currently must be a valid IPv4 address."
- type: str
- mtu:
- description:
- - MTU setting for the subnet.
- default: 1500
- type: int
- group_type:
- description:
- - The type of network interface group.
- type: str
- default: eth
- choices: [ eth ]
- prefix:
- description:
- - "Network prefix in CIDR notation.
- Required to create a new network interface group.
- Currently only IPv4 addresses with subnet mask are supported."
- type: str
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new network interface group foo in AZ bar
- purestorage.fusion.fusion_nig:
- name: foo
- availability_zone: bar
- region: region1
- mtu: 9000
- gateway: 10.21.200.1
- prefix: 10.21.200.0/24
- state: present
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete network interface group foo in AZ bar
- purestorage.fusion.fusion_nig:
- name: foo
- availability_zone: bar
- region: region1
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.networking import (
- is_valid_address,
- is_valid_network,
- is_address_in_network,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-
-
-def get_nig(module, fusion):
- """Check Network Interface Group"""
- nig_api_instance = purefusion.NetworkInterfaceGroupsApi(fusion)
- try:
- return nig_api_instance.get_network_interface_group(
- availability_zone_name=module.params["availability_zone"],
- region_name=module.params["region"],
- network_interface_group_name=module.params["name"],
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def create_nig(module, fusion):
- """Create Network Interface Group"""
-
- nig_api_instance = purefusion.NetworkInterfaceGroupsApi(fusion)
-
- changed = False
- if module.params["gateway"] and not is_address_in_network(
- module.params["gateway"], module.params["prefix"]
- ):
- module.fail_json(msg="`gateway` must be an address in subnet `prefix`")
-
- id = None
- if not module.check_mode:
- display_name = module.params["display_name"] or module.params["name"]
- if module.params["group_type"] == "eth":
- if module.params["gateway"]:
- eth = purefusion.NetworkInterfaceGroupEthPost(
- prefix=module.params["prefix"],
- gateway=module.params["gateway"],
- mtu=module.params["mtu"],
- )
- else:
- eth = purefusion.NetworkInterfaceGroupEthPost(
- prefix=module.params["prefix"],
- mtu=module.params["mtu"],
- )
- nig = purefusion.NetworkInterfaceGroupPost(
- group_type="eth",
- eth=eth,
- name=module.params["name"],
- display_name=display_name,
- )
- op = nig_api_instance.create_network_interface_group(
- nig,
- availability_zone_name=module.params["availability_zone"],
- region_name=module.params["region"],
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
- changed = True
- else:
- # to prevent future unintended error
- module.warn(f"group_type={module.params['group_type']} is not implemented")
-
- module.exit_json(changed=changed, id=id)
-
-
-def delete_nig(module, fusion):
- """Delete Network Interface Group"""
- changed = True
- nig_api_instance = purefusion.NetworkInterfaceGroupsApi(fusion)
- if not module.check_mode:
- op = nig_api_instance.delete_network_interface_group(
- availability_zone_name=module.params["availability_zone"],
- region_name=module.params["region"],
- network_interface_group_name=module.params["name"],
- )
- await_operation(fusion, op)
- module.exit_json(changed=changed)
-
-
-def update_nig(module, fusion, nig):
- """Update Network Interface Group"""
-
- nifg_api_instance = purefusion.NetworkInterfaceGroupsApi(fusion)
- patches = []
- if (
- module.params["display_name"]
- and module.params["display_name"] != nig.display_name
- ):
- patch = purefusion.NetworkInterfaceGroupPatch(
- display_name=purefusion.NullableString(module.params["display_name"]),
- )
- patches.append(patch)
-
- if not module.check_mode:
- for patch in patches:
- op = nifg_api_instance.update_network_interface_group(
- patch,
- availability_zone_name=module.params["availability_zone"],
- region_name=module.params["region"],
- network_interface_group_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- changed = len(patches) != 0
-
- module.exit_json(changed=changed, id=nig.id)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- display_name=dict(type="str"),
- availability_zone=dict(type="str", required=True, aliases=["az"]),
- region=dict(type="str", required=True),
- prefix=dict(type="str"),
- gateway=dict(type="str"),
- mtu=dict(type="int", default=1500),
- group_type=dict(type="str", default="eth", choices=["eth"]),
- state=dict(type="str", default="present", choices=["absent", "present"]),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- if module.params["prefix"] and not is_valid_network(module.params["prefix"]):
- module.fail_json(
- msg="`prefix` '{0}' is not a valid address in CIDR notation".format(
- module.params["prefix"]
- )
- )
- if module.params["gateway"] and not is_valid_address(module.params["gateway"]):
- module.fail_json(
- msg="`gateway` '{0}' is not a valid address".format(
- module.params["gateway"]
- )
- )
-
- nig = get_nig(module, fusion)
-
- if state == "present" and not nig:
- module.fail_on_missing_params(["prefix"])
- create_nig(module, fusion)
- elif state == "present" and nig:
- update_nig(module, fusion, nig)
- elif state == "absent" and nig:
- delete_nig(module, fusion)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_pg.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_pg.py
deleted file mode 100644
index 6d6f0eb94..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_pg.py
+++ /dev/null
@@ -1,307 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_pg
-version_added: '1.0.0'
-short_description: Manage placement groups in Pure Storage Fusion
-description:
-- Create, update or delete a placement groups in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the placement group.
- type: str
- required: true
- display_name:
- description:
- - The human name of the placement group.
- - If not provided, defaults to I(name).
- type: str
- state:
- description:
- - Define whether the placement group should exist or not.
- type: str
- default: present
- choices: [ absent, present ]
- destroy_snapshots_on_delete:
- description:
- - "Before deleting placement group, snapshots within the placement group will be deleted."
- - "If `false` then any snapshots will need to be deleted as a separate step before removing the placement group."
- type: bool
- tenant:
- description:
- - The name of the tenant.
- type: str
- required: true
- tenant_space:
- description:
- - The name of the tenant space.
- type: str
- required: true
- region:
- description:
- - The name of the region the availability zone is in.
- type: str
- availability_zone:
- aliases: [ az ]
- description:
- - The name of the availability zone the placement group is in.
- type: str
- storage_service:
- description:
- - The name of the storage service to create the placement group for.
- type: str
- array:
- description:
- - "Array to place the placement group to. Changing it (i.e. manual migration)
- is an elevated operation."
- type: str
- placement_engine:
- description:
- - For workload placement recommendations from Pure1 Meta, use C(pure1meta).
- - Please note that this might increase volume creation time.
- type: str
- choices: [ heuristics, pure1meta ]
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new placement group named foo
- purestorage.fusion.fusion_pg:
- name: foo
- tenant: test
- tenant_space: space_1
- availability_zone: az1
- region: region1
- storage_service: storage_service_1
- state: present
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete placement group foo
- purestorage.fusion.fusion_pg:
- name: foo
- tenant: test
- tenant_space: space_1
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.snapshots import (
- delete_snapshot,
-)
-
-
-def get_pg(module, fusion):
- """Return Placement Group or None"""
- pg_api_instance = purefusion.PlacementGroupsApi(fusion)
- try:
- return pg_api_instance.get_placement_group(
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["tenant_space"],
- placement_group_name=module.params["name"],
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def create_pg(module, fusion):
- """Create Placement Group"""
-
- pg_api_instance = purefusion.PlacementGroupsApi(fusion)
-
- if not module.check_mode:
- if not module.params["display_name"]:
- display_name = module.params["name"]
- else:
- display_name = module.params["display_name"]
- group = purefusion.PlacementGroupPost(
- availability_zone=module.params["availability_zone"],
- name=module.params["name"],
- display_name=display_name,
- region=module.params["region"],
- storage_service=module.params["storage_service"],
- )
- op = pg_api_instance.create_placement_group(
- group,
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["tenant_space"],
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- return True, id
-
-
-def update_display_name(module, fusion, patches, pg):
- if not module.params["display_name"]:
- return
- if module.params["display_name"] == pg.display_name:
- return
- patch = purefusion.PlacementGroupPatch(
- display_name=purefusion.NullableString(module.params["display_name"]),
- )
- patches.append(patch)
-
-
-def update_array(module, fusion, patches, pg):
- if not module.params["array"]:
- return
- if not pg.array:
- module.warn(
- "cannot see placement group array, probably missing required permissions to change it"
- )
- return
- if pg.array.name == module.params["array"]:
- return
-
- patch = purefusion.PlacementGroupPatch(
- array=purefusion.NullableString(module.params["array"]),
- )
- patches.append(patch)
-
-
-def update_pg(module, fusion, pg):
- """Update Placement Group"""
-
- pg_api_instance = purefusion.PlacementGroupsApi(fusion)
- patches = []
-
- update_display_name(module, fusion, patches, pg)
- update_array(module, fusion, patches, pg)
-
- if not module.check_mode:
- for patch in patches:
- op = pg_api_instance.update_placement_group(
- patch,
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["tenant_space"],
- placement_group_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- changed = len(patches) != 0
- return changed
-
-
-def delete_pg(module, fusion):
- """Delete Placement Group"""
- pg_api_instance = purefusion.PlacementGroupsApi(fusion)
- if not module.check_mode:
- if module.params["destroy_snapshots_on_delete"]:
- snapshots_api = purefusion.SnapshotsApi(fusion)
- snapshots = snapshots_api.list_snapshots(
- placement_group=module.params["name"],
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["tenant_space"],
- )
- for snap in snapshots.items:
- delete_snapshot(fusion, snap, snapshots_api)
-
- op = pg_api_instance.delete_placement_group(
- placement_group_name=module.params["name"],
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["tenant_space"],
- )
- await_operation(fusion, op)
-
- return True
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- destroy_snapshots_on_delete=dict(type="bool"),
- display_name=dict(type="str"),
- tenant=dict(type="str", required=True),
- tenant_space=dict(type="str", required=True),
- region=dict(type="str"),
- availability_zone=dict(type="str", aliases=["az"]),
- storage_service=dict(type="str"),
- state=dict(type="str", default="present", choices=["absent", "present"]),
- array=dict(type="str"),
- placement_engine=dict(
- type="str",
- choices=["heuristics", "pure1meta"],
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- ),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- if module.params["placement_engine"]:
- module.warn("placement_engine parameter will be deprecated in version 2.0.0")
-
- changed = False
-
- state = module.params["state"]
- pgroup = get_pg(module, fusion)
-
- id = None
- if pgroup is not None:
- id = pgroup.id
-
- if state == "present" and not pgroup:
- module.fail_on_missing_params(
- ["region", "availability_zone", "storage_service"]
- )
- changed, id = create_pg(module, fusion) or changed
- if module.params["array"]:
- # changing placement requires additional update
- pgroup = get_pg(module, fusion)
- changedUpdate = update_pg(module, fusion, pgroup)
- changed = changed | changedUpdate
- elif state == "present" and pgroup:
- changed = update_pg(module, fusion, pgroup) or changed
- elif state == "absent" and pgroup:
- changed = delete_pg(module, fusion) or changed
- module.exit_json(changed=changed)
-
- if id is not None:
- module.exit_json(changed=changed, id=id)
-
- module.exit_json(changed=changed)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_pp.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_pp.py
deleted file mode 100644
index 216209d84..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_pp.py
+++ /dev/null
@@ -1,208 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_pp
-version_added: '1.0.0'
-short_description: Manage protection policies in Pure Storage Fusion
-description:
-- Manage protection policies in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the protection policy.
- type: str
- required: true
- state:
- description:
- - Define whether the protection policy should exist or not.
- default: present
- choices: [ present, absent ]
- type: str
- destroy_snapshots_on_delete:
- description:
- - "Before deleting protection policy, snapshots within the protection policy will be deleted."
- - "If `false` then any snapshots will need to be deleted as a separate step before removing the protection policy."
- type: bool
- display_name:
- description:
- - The human name of the protection policy.
- - If not provided, defaults to I(name).
- type: str
- local_rpo:
- description:
- - Recovery Point Objective for snapshots.
- - Minimum value is 10 minutes.
- - Value can be provided as m(inutes), h(ours),
- d(ays), w(eeks), or y(ears).
- - If no unit is provided, minutes are assumed.
- type: str
- local_retention:
- description:
- - Retention Duration for periodic snapshots.
- - Minimum value is 10 minutes.
- - Value can be provided as m(inutes), h(ours),
- d(ays), w(eeks), or y(ears).
- - If no unit is provided, minutes are assumed.
- type: str
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new protection policy foo
- purestorage.fusion.fusion_pp:
- name: foo
- local_rpo: 10
- local_retention: 4d
- display_name: "foo pp"
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete protection policy foo
- purestorage.fusion.fusion_pp:
- name: foo
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.parsing import (
- parse_minutes,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.snapshots import (
- delete_snapshot,
-)
-
-
-def get_pp(module, fusion):
- """Return Protection Policy or None"""
- pp_api_instance = purefusion.ProtectionPoliciesApi(fusion)
- try:
- return pp_api_instance.get_protection_policy(
- protection_policy_name=module.params["name"]
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def create_pp(module, fusion):
- """Create Protection Policy"""
-
- pp_api_instance = purefusion.ProtectionPoliciesApi(fusion)
- local_rpo = parse_minutes(module, module.params["local_rpo"])
- local_retention = parse_minutes(module, module.params["local_retention"])
- if local_retention < 10:
- module.fail_json(msg="Local Retention must be a minimum of 10 minutes")
- if local_rpo < 10:
- module.fail_json(msg="Local RPO must be a minimum of 10 minutes")
-
- changed = True
- id = None
- if not module.check_mode:
- if not module.params["display_name"]:
- display_name = module.params["name"]
- else:
- display_name = module.params["display_name"]
- op = pp_api_instance.create_protection_policy(
- purefusion.ProtectionPolicyPost(
- name=module.params["name"],
- display_name=display_name,
- objectives=[
- purefusion.RPO(type="RPO", rpo="PT" + str(local_rpo) + "M"),
- purefusion.Retention(
- type="Retention", after="PT" + str(local_retention) + "M"
- ),
- ],
- )
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- module.exit_json(changed=changed, id=id)
-
-
-def delete_pp(module, fusion):
- """Delete Protection Policy"""
- pp_api_instance = purefusion.ProtectionPoliciesApi(fusion)
- changed = True
- if not module.check_mode:
- if module.params["destroy_snapshots_on_delete"]:
- protection_policy = get_pp(module, fusion)
- snapshots_api = purefusion.SnapshotsApi(fusion)
- snapshots = snapshots_api.query_snapshots(
- protection_policy_id=protection_policy.id
- )
- for snap in snapshots.items:
- delete_snapshot(fusion, snap, snapshots_api)
-
- op = pp_api_instance.delete_protection_policy(
- protection_policy_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- module.exit_json(changed=changed)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- destroy_snapshots_on_delete=dict(type="bool"),
- display_name=dict(type="str"),
- local_rpo=dict(type="str"),
- local_retention=dict(type="str"),
- state=dict(type="str", default="present", choices=["present", "absent"]),
- )
- )
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- policy = get_pp(module, fusion)
-
- if not policy and state == "present":
- module.fail_on_missing_params(["local_rpo", "local_retention"])
- create_pp(module, fusion)
- elif policy and state == "absent":
- delete_pp(module, fusion)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ra.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ra.py
deleted file mode 100644
index c2ae2d5cf..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ra.py
+++ /dev/null
@@ -1,284 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_ra
-version_added: '1.0.0'
-short_description: Manage role assignments in Pure Storage Fusion
-description:
-- Create or delete a storage class in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- role:
- description:
- - The name of the role to be assigned/unassigned.
- type: str
- required: true
- state:
- description:
- - Define whether the role assingment should exist or not.
- type: str
- default: present
- choices: [ absent, present ]
- user:
- description:
- - The username to assign the role to.
- - Currently this only supports the Pure1 App ID.
- - This should be provide in the same format as I(issuer_id).
- type: str
- principal:
- description:
- - The unique ID of the principal (User or API Client) to assign to the role.
- type: str
- api_client_key:
- description:
- - The issuer ID of the API client to assign the role to.
- type: str
- scope:
- description:
- - The level to which the role is assigned.
- choices: [ organization, tenant, tenant_space ]
- default: organization
- type: str
- tenant:
- description:
- - The name of the tenant the user has the role applied to.
- - Must be provided if I(scope) is set to either C(tenant) or C(tenant_space).
- type: str
- tenant_space:
- description:
- - The name of the tenant_space the user has the role applied to.
- - Must be provided if I(scope) is set to C(tenant_space).
- type: str
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Assign role foo to user in tenant bar
- purestorage.fusion.fusion_ra:
- name: foo
- user: key_name
- tenant: bar
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete role foo from user in tenant bar
- purestorage.fusion.fusion_ra:
- name: foo
- user: key_name
- tenant: bar
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-
-
-def get_principal(module, fusion):
- if module.params["principal"]:
- return module.params["principal"]
- if module.params["user"]:
- principal = user_to_principal(fusion, module.params["user"])
- if not principal:
- module.fail_json(
- msg="User {0} does not exist".format(module.params["user"])
- )
- return principal
- if module.params["api_client_key"]:
- principal = apiclient_to_principal(fusion, module.params["api_client_key"])
- if not principal:
- module.fail_json(
- msg="API Client with key {0} does not exist".format(
- module.params["api_client_key"]
- )
- )
- return principal
-
-
-def user_to_principal(fusion, user_id):
- """Given a human-readable Fusion user, such as a Pure 1 App ID
- return the associated principal
- """
- id_api_instance = purefusion.IdentityManagerApi(fusion)
- users = id_api_instance.list_users()
- for user in users:
- if user.name == user_id:
- return user.id
- return None
-
-
-def apiclient_to_principal(fusion, api_client_key):
- """Given an API client issuer ID, such as "pure1:apikey:123xXxyYyzYzASDF",
- return the associated principal
- """
- id_api_instance = purefusion.IdentityManagerApi(fusion)
- api_clients = id_api_instance.list_users(name=api_client_key)
- if len(api_clients) > 0:
- return api_clients[0].id
- return None
-
-
-def get_scope(params):
- """Given a scope type and associated tenant
- and tenant_space, return the scope_link
- """
- scope_link = None
- if params["scope"] == "organization":
- scope_link = "/"
- elif params["scope"] == "tenant":
- scope_link = "/tenants/" + params["tenant"]
- elif params["scope"] == "tenant_space":
- scope_link = (
- "/tenants/" + params["tenant"] + "/tenant-spaces/" + params["tenant_space"]
- )
- return scope_link
-
-
-def get_ra(module, fusion):
- """Return Role Assignment or None"""
- ra_api_instance = purefusion.RoleAssignmentsApi(fusion)
- try:
- principal = get_principal(module, fusion)
- assignments = ra_api_instance.list_role_assignments(
- role_name=module.params["role"],
- principal=principal,
- )
- for assign in assignments:
- scope = get_scope(module.params)
- if assign.scope.self_link == scope:
- return assign
- return None
- except purefusion.rest.ApiException:
- return None
-
-
-def create_ra(module, fusion):
- """Create Role Assignment"""
-
- ra_api_instance = purefusion.RoleAssignmentsApi(fusion)
-
- changed = True
- id = None
- if not module.check_mode:
- principal = get_principal(module, fusion)
- scope = get_scope(module.params)
- assignment = purefusion.RoleAssignmentPost(scope=scope, principal=principal)
- op = ra_api_instance.create_role_assignment(
- assignment, role_name=module.params["role"]
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- module.exit_json(changed=changed, id=id)
-
-
-def delete_ra(module, fusion):
- """Delete Role Assignment"""
- changed = True
- ra_api_instance = purefusion.RoleAssignmentsApi(fusion)
- if not module.check_mode:
- ra_name = get_ra(module, fusion).name
- op = ra_api_instance.delete_role_assignment(
- role_name=module.params["role"], role_assignment_name=ra_name
- )
- await_operation(fusion, op)
-
- module.exit_json(changed=changed)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- api_client_key=dict(type="str", no_log=True),
- principal=dict(type="str"),
- role=dict(
- type="str",
- required=True,
- deprecated_aliases=[
- dict(
- name="name",
- date="2023-07-26",
- collection_name="purefusion.fusion",
- )
- ],
- ),
- scope=dict(
- type="str",
- default="organization",
- choices=["organization", "tenant", "tenant_space"],
- ),
- state=dict(type="str", default="present", choices=["present", "absent"]),
- tenant=dict(type="str"),
- tenant_space=dict(type="str"),
- user=dict(type="str"),
- )
- )
-
- required_if = [
- ["scope", "tenant", ["tenant"]],
- ["scope", "tenant_space", ["tenant", "tenant_space"]],
- ]
- mutually_exclusive = [
- ("user", "principal", "api_client_key"),
- ]
- required_one_of = [
- ("user", "principal", "api_client_key"),
- ]
-
- module = AnsibleModule(
- argument_spec,
- required_if=required_if,
- supports_check_mode=True,
- mutually_exclusive=mutually_exclusive,
- required_one_of=required_one_of,
- )
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- role_assignment = get_ra(module, fusion)
-
- if not role_assignment and state == "present":
- create_ra(module, fusion)
- elif role_assignment and state == "absent":
- delete_ra(module, fusion)
- else:
- module.exit_json(changed=False)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_region.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_region.py
deleted file mode 100644
index de40e7dc2..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_region.py
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_region
-version_added: '1.1.0'
-short_description: Manage Regions in Pure Storage Fusion
-description:
-- Manage regions in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the Region.
- type: str
- required: true
- state:
- description:
- - Define whether the Region should exist or not.
- default: present
- choices: [ present, absent ]
- type: str
- display_name:
- description:
- - The human name of the Region.
- - If not provided, defaults to I(name).
- type: str
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new region foo
- purestorage.fusion.fusion_region:
- name: foo
- display_name: "foo Region"
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Update region foo
- purestorage.fusion.fusion_region:
- name: foo
- display_name: "new foo Region"
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete region foo
- purestorage.fusion.fusion_region:
- name: foo
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils import getters
-
-
-def get_region(module, fusion):
- """Get Region or None"""
- return getters.get_region(module, fusion, module.params["name"])
-
-
-def create_region(module, fusion):
- """Create Region"""
-
- reg_api_instance = purefusion.RegionsApi(fusion)
-
- changed = True
- id = None
- if not module.check_mode:
- if not module.params["display_name"]:
- display_name = module.params["name"]
- else:
- display_name = module.params["display_name"]
- region = purefusion.RegionPost(
- name=module.params["name"],
- display_name=display_name,
- )
- op = reg_api_instance.create_region(region)
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- module.exit_json(changed=changed, id=id)
-
-
-def delete_region(module, fusion):
- """Delete Region"""
-
- reg_api_instance = purefusion.RegionsApi(fusion)
-
- changed = True
- if not module.check_mode:
- op = reg_api_instance.delete_region(region_name=module.params["name"])
- await_operation(fusion, op)
-
- module.exit_json(changed=changed)
-
-
-def update_region(module, fusion, region):
- """Update Region settings"""
- changed = False
- reg_api_instance = purefusion.RegionsApi(fusion)
-
- if (
- module.params["display_name"]
- and module.params["display_name"] != region.display_name
- ):
- changed = True
- if not module.check_mode:
- reg = purefusion.RegionPatch(
- display_name=purefusion.NullableString(module.params["display_name"])
- )
- op = reg_api_instance.update_region(
- reg,
- region_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- module.exit_json(changed=changed, id=region.id)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- display_name=dict(type="str"),
- state=dict(type="str", default="present", choices=["present", "absent"]),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- region = get_region(module, fusion)
-
- if not region and state == "present":
- create_region(module, fusion)
- elif region and state == "present":
- update_region(module, fusion, region)
- elif region and state == "absent":
- delete_region(module, fusion)
- else:
- module.exit_json(changed=False)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_sc.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_sc.py
deleted file mode 100644
index 59fc0025e..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_sc.py
+++ /dev/null
@@ -1,257 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_sc
-version_added: '1.0.0'
-short_description: Manage storage classes in Pure Storage Fusion
-description:
-- Manage a storage class in Pure Storage Fusion.
-notes:
-- Supports C(check_mode).
-- It is not currently possible to update bw_limit or
- iops_limit after a storage class has been created.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-options:
- name:
- description:
- - The name of the storage class.
- type: str
- required: true
- state:
- description:
- - Define whether the storage class should exist or not.
- default: present
- choices: [ present, absent ]
- type: str
- display_name:
- description:
- - The human name of the storage class.
- - If not provided, defaults to I(name).
- type: str
- size_limit:
- description:
- - Volume size limit in M, G, T or P units.
- - Must be between 1MB and 4PB.
- - If not provided at creation, this will default to 4PB.
- type: str
- bw_limit:
- description:
- - The bandwidth limit in M or G units.
- M will set MB/s.
- G will set GB/s.
- - Must be between 1MB/s and 512GB/s.
- - If not provided at creation, this will default to 512GB/s.
- type: str
- iops_limit:
- description:
- - The IOPs limit - use value or K or M.
- K will mean 1000.
- M will mean 1000000.
- - Must be between 100 and 100000000.
- - If not provided at creation, this will default to 100000000.
- type: str
- storage_service:
- description:
- - Storage service to which the storage class belongs.
- type: str
- required: true
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new storage class foo
- purestorage.fusion.fusion_sc:
- name: foo
- size_limit: 100G
- iops_limit: 100000
- bw_limit: 25M
- storage_service: service1
- display_name: "test class"
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Update storage class (only display_name change is supported)
- purestorage.fusion.fusion_sc:
- name: foo
- display_name: "main class"
- storage_service: service1
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete storage class
- purestorage.fusion.fusion_sc:
- name: foo
- storage_service: service1
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.parsing import (
- parse_number_with_metric_suffix,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-
-
-def get_sc(module, fusion):
- """Return Storage Class or None"""
- sc_api_instance = purefusion.StorageClassesApi(fusion)
- try:
- return sc_api_instance.get_storage_class(
- storage_class_name=module.params["name"],
- storage_service_name=module.params["storage_service"],
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def create_sc(module, fusion):
- """Create Storage Class"""
-
- sc_api_instance = purefusion.StorageClassesApi(fusion)
-
- if not module.params["size_limit"]:
- module.params["size_limit"] = "4P"
- if not module.params["iops_limit"]:
- module.params["iops_limit"] = "100000000"
- if not module.params["bw_limit"]:
- module.params["bw_limit"] = "512G"
- size_limit = parse_number_with_metric_suffix(module, module.params["size_limit"])
- iops_limit = int(
- parse_number_with_metric_suffix(
- module, module.params["iops_limit"], factor=1000
- )
- )
- bw_limit = parse_number_with_metric_suffix(module, module.params["bw_limit"])
- if bw_limit < 1048576 or bw_limit > 549755813888: # 1MB/s to 512GB/s
- module.fail_json(msg="Bandwidth limit is not within the required range")
- if iops_limit < 100 or iops_limit > 100_000_000:
- module.fail_json(msg="IOPs limit is not within the required range")
- if size_limit < 1048576 or size_limit > 4503599627370496: # 1MB to 4PB
- module.fail_json(msg="Size limit is not within the required range")
-
- changed = True
- id = None
- if not module.check_mode:
- if not module.params["display_name"]:
- display_name = module.params["name"]
- else:
- display_name = module.params["display_name"]
- s_class = purefusion.StorageClassPost(
- name=module.params["name"],
- size_limit=size_limit,
- iops_limit=iops_limit,
- bandwidth_limit=bw_limit,
- display_name=display_name,
- )
- op = sc_api_instance.create_storage_class(
- s_class, storage_service_name=module.params["storage_service"]
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- module.exit_json(changed=changed, id=id)
-
-
-def update_sc(module, fusion, s_class):
- """Update Storage Class settings"""
- changed = False
- sc_api_instance = purefusion.StorageClassesApi(fusion)
-
- if (
- module.params["display_name"]
- and module.params["display_name"] != s_class.display_name
- ):
- changed = True
- if not module.check_mode:
- sclass = purefusion.StorageClassPatch(
- display_name=purefusion.NullableString(module.params["display_name"])
- )
- op = sc_api_instance.update_storage_class(
- sclass,
- storage_service_name=module.params["storage_service"],
- storage_class_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- module.exit_json(changed=changed, id=s_class.id)
-
-
-def delete_sc(module, fusion):
- """Delete Storage Class"""
- sc_api_instance = purefusion.StorageClassesApi(fusion)
- changed = True
- if not module.check_mode:
- op = sc_api_instance.delete_storage_class(
- storage_class_name=module.params["name"],
- storage_service_name=module.params["storage_service"],
- )
- await_operation(fusion, op)
-
- module.exit_json(changed=changed)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- display_name=dict(type="str"),
- iops_limit=dict(type="str"),
- bw_limit=dict(type="str"),
- size_limit=dict(type="str"),
- storage_service=dict(type="str", required=True),
- state=dict(type="str", default="present", choices=["present", "absent"]),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- s_class = get_sc(module, fusion)
-
- if not s_class and state == "present":
- create_sc(module, fusion)
- elif s_class and state == "present":
- update_sc(module, fusion, s_class)
- elif s_class and state == "absent":
- delete_sc(module, fusion)
- else:
- module.exit_json(changed=False)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_se.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_se.py
deleted file mode 100644
index 3a191a166..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_se.py
+++ /dev/null
@@ -1,508 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2023, Simon Dodsley (simon@purestorage.com), Jan Kodera (jkodera@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_se
-version_added: '1.0.0'
-short_description: Manage storage endpoints in Pure Storage Fusion
-description:
-- Create or delete storage endpoints in Pure Storage Fusion.
-notes:
-- Supports C(check_mode).
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-options:
- name:
- description:
- - The name of the storage endpoint.
- type: str
- required: true
- display_name:
- description:
- - The human name of the storage endpoint.
- - If not provided, defaults to I(name).
- type: str
- state:
- description:
- - Define whether the storage endpoint should exist or not.
- type: str
- default: present
- choices: [ absent, present ]
- region:
- description:
- - The name of the region the availability zone is in
- type: str
- required: true
- availability_zone:
- aliases: [ az ]
- description:
- - The name of the availability zone for the storage endpoint.
- type: str
- required: true
- endpoint_type:
- description:
- - "DEPRECATED: Will be removed in version 2.0.0"
- - Type of the storage endpoint. Only iSCSI is available at the moment.
- type: str
- iscsi:
- description:
- - List of discovery interfaces.
- type: list
- elements: dict
- suboptions:
- address:
- description:
- - IP address to be used in the subnet of the storage endpoint.
- - IP address must include a CIDR notation.
- - Only IPv4 is supported at the moment.
- type: str
- gateway:
- description:
- - Address of the subnet gateway.
- type: str
- network_interface_groups:
- description:
- - List of network interface groups to assign to the address.
- type: list
- elements: str
- cbs_azure_iscsi:
- description:
- - CBS Azure iSCSI
- type: dict
- suboptions:
- storage_endpoint_collection_identity:
- description:
- - The Storage Endpoint Collection Identity which belongs to the Azure entities.
- type: str
- load_balancer:
- description:
- - The Load Balancer id which gives permissions to CBS array applications to modify the Load Balancer.
- type: str
- load_balancer_addresses:
- description:
- - The IPv4 addresses of the Load Balancer.
- type: list
- elements: str
- network_interface_groups:
- description:
- - "DEPRECATED: Will be removed in version 2.0.0"
- - List of network interface groups to assign to the storage endpoints.
- type: list
- elements: str
- addresses:
- description:
- - "DEPRECATED: Will be removed in version 2.0.0"
- - List of IP addresses to be used in the subnet of the storage endpoint.
- - IP addresses must include a CIDR notation.
- - Only IPv4 is supported at the moment.
- type: list
- elements: str
- gateway:
- description:
- - "DEPRECATED: Will be removed in version 2.0.0"
- - Address of the subnet gateway.
- - Currently this must be provided.
- type: str
-
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new storage endpoint foo in AZ bar
- purestorage.fusion.fusion_se:
- name: foo
- availability_zone: bar
- region: us-west
- iscsi:
- - address: 10.21.200.124/24
- gateway: 10.21.200.1
- network_interface_groups:
- - subnet-0
- - address: 10.21.200.36/24
- gateway: 10.21.200.2
- network_interface_groups:
- - subnet-0
- - subnet-1
- state: present
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Create new CBS storage endpoint foo in AZ bar
- purestorage.fusion.fusion_se:
- name: foo
- availability_zone: bar
- region: us-west
- cbs_azure_iscsi:
- storage_endpoint_collection_identity: "/subscriptions/sub/resourcegroups/sec/providers/ms/userAssignedIdentities/secId"
- load_balancer: "/subscriptions/sub/resourcegroups/sec/providers/ms/loadBalancers/sec-lb"
- load_balancer_addresses:
- - 10.21.200.1
- - 10.21.200.2
- state: present
- app_id: key_name
- key_file: "az-admin-private-key.pem"
-
-- name: Delete storage endpoint foo in AZ bar
- purestorage.fusion.fusion_se:
- name: foo
- availability_zone: bar
- region: us-west
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: (DEPRECATED) Create new storage endpoint foo in AZ bar
- purestorage.fusion.fusion_se:
- name: foo
- availability_zone: bar
- gateway: 10.21.200.1
- region: us-west
- addresses:
- - 10.21.200.124/24
- - 10.21.200.36/24
- network_interface_groups:
- - subnet-0
- state: present
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.networking import (
- is_valid_network,
- is_valid_address,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-
-
-#######################################################################
-# DEPRECATED CODE SECTION STARTS
-
-
-def create_se_old(module, fusion):
- """Create Storage Endpoint"""
-
- se_api_instance = purefusion.StorageEndpointsApi(fusion)
-
- changed = True
-
- if not module.check_mode:
- if not module.params["display_name"]:
- display_name = module.params["name"]
- else:
- display_name = module.params["display_name"]
- ifaces = []
- for address in module.params["addresses"]:
- if module.params["gateway"]:
- iface = purefusion.StorageEndpointIscsiDiscoveryInterfacePost(
- address=address,
- gateway=module.params["gateway"],
- network_interface_groups=module.params["network_interface_groups"],
- )
- else:
- iface = purefusion.StorageEndpointIscsiDiscoveryInterfacePost(
- address=address,
- network_interface_groups=module.params["network_interface_groups"],
- )
- ifaces.append(iface)
- op = purefusion.StorageEndpointPost(
- endpoint_type="iscsi",
- iscsi=purefusion.StorageEndpointIscsiPost(
- discovery_interfaces=ifaces,
- ),
- name=module.params["name"],
- display_name=display_name,
- )
- op = se_api_instance.create_storage_endpoint(
- op,
- region_name=module.params["region"],
- availability_zone_name=module.params["availability_zone"],
- )
- await_operation(fusion, op)
-
- module.exit_json(changed=changed)
-
-
-# DEPRECATED CODE SECTION ENDS
-#######################################################################
-
-
-def get_se(module, fusion):
- """Storage Endpoint or None"""
- se_api_instance = purefusion.StorageEndpointsApi(fusion)
- try:
- return se_api_instance.get_storage_endpoint(
- region_name=module.params["region"],
- storage_endpoint_name=module.params["name"],
- availability_zone_name=module.params["availability_zone"],
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def create_se(module, fusion):
- """Create Storage Endpoint"""
- se_api_instance = purefusion.StorageEndpointsApi(fusion)
- id = None
- if not module.check_mode:
- endpoint_type = None
-
- iscsi = None
- if module.params["iscsi"] is not None:
- iscsi = purefusion.StorageEndpointIscsiPost(
- discovery_interfaces=[
- purefusion.StorageEndpointIscsiDiscoveryInterfacePost(**endpoint)
- for endpoint in module.params["iscsi"]
- ]
- )
- endpoint_type = "iscsi"
-
- cbs_azure_iscsi = None
- if module.params["cbs_azure_iscsi"] is not None:
- cbs_azure_iscsi = purefusion.StorageEndpointCbsAzureIscsiPost(
- storage_endpoint_collection_identity=module.params["cbs_azure_iscsi"][
- "storage_endpoint_collection_identity"
- ],
- load_balancer=module.params["cbs_azure_iscsi"]["load_balancer"],
- load_balancer_addresses=module.params["cbs_azure_iscsi"][
- "load_balancer_addresses"
- ],
- )
- endpoint_type = "cbs-azure-iscsi"
-
- op = se_api_instance.create_storage_endpoint(
- purefusion.StorageEndpointPost(
- name=module.params["name"],
- display_name=module.params["display_name"] or module.params["name"],
- endpoint_type=endpoint_type,
- iscsi=iscsi,
- cbs_azure_iscsi=cbs_azure_iscsi,
- ),
- region_name=module.params["region"],
- availability_zone_name=module.params["availability_zone"],
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- module.exit_json(changed=True, id=id)
-
-
-def delete_se(module, fusion):
- """Delete Storage Endpoint"""
- se_api_instance = purefusion.StorageEndpointsApi(fusion)
- if not module.check_mode:
- op = se_api_instance.delete_storage_endpoint(
- region_name=module.params["region"],
- availability_zone_name=module.params["availability_zone"],
- storage_endpoint_name=module.params["name"],
- )
- await_operation(fusion, op)
- module.exit_json(changed=True)
-
-
-def update_se(module, fusion, se):
- """Update Storage Endpoint"""
-
- se_api_instance = purefusion.StorageEndpointsApi(fusion)
- patches = []
- if (
- module.params["display_name"]
- and module.params["display_name"] != se.display_name
- ):
- patch = purefusion.StorageEndpointPatch(
- display_name=purefusion.NullableString(module.params["display_name"]),
- )
- patches.append(patch)
-
- if not module.check_mode:
- for patch in patches:
- op = se_api_instance.update_storage_endpoint(
- patch,
- region_name=module.params["region"],
- availability_zone_name=module.params["availability_zone"],
- storage_endpoint_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- changed = len(patches) != 0
-
- module.exit_json(changed=changed, id=se.id)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- display_name=dict(type="str"),
- region=dict(type="str", required=True),
- availability_zone=dict(type="str", required=True, aliases=["az"]),
- iscsi=dict(
- type="list",
- elements="dict",
- options=dict(
- address=dict(type="str"),
- gateway=dict(type="str"),
- network_interface_groups=dict(type="list", elements="str"),
- ),
- ),
- cbs_azure_iscsi=dict(
- type="dict",
- options=dict(
- storage_endpoint_collection_identity=dict(type="str"),
- load_balancer=dict(type="str"),
- load_balancer_addresses=dict(type="list", elements="str"),
- ),
- ),
- state=dict(type="str", default="present", choices=["absent", "present"]),
- # deprecated, will be removed in 2.0.0
- endpoint_type=dict(
- type="str",
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- ),
- addresses=dict(
- type="list",
- elements="str",
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- ),
- gateway=dict(
- type="str",
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- ),
- network_interface_groups=dict(
- type="list",
- elements="str",
- removed_in_version="2.0.0",
- removed_from_collection="purestorage.fusion",
- ),
- )
- )
-
- mutually_exclusive = [
- ("iscsi", "cbs_azure_iscsi"),
- # can not use both deprecated and new fields at the same time
- ("iscsi", "cbs_azure_iscsi", "addresses"),
- ("iscsi", "cbs_azure_iscsi", "gateway"),
- ("iscsi", "cbs_azure_iscsi", "network_interface_groups"),
- ]
-
- module = AnsibleModule(
- argument_spec,
- mutually_exclusive=mutually_exclusive,
- supports_check_mode=True,
- )
- fusion = setup_fusion(module)
-
- state = module.params["state"]
-
- if module.params["endpoint_type"] is not None:
- module.warn(
- "'endpoint_type' parameter is deprecated and will be removed in the version 2.0"
- )
-
- deprecated_parameters = {"addresses", "gateway", "network_interface_groups"}
- used_deprecated_parameters = [
- key
- for key in list(deprecated_parameters & module.params.keys())
- if module.params[key] is not None
- ]
-
- if len(used_deprecated_parameters) > 0:
- # user uses deprecated module interface
- for param_name in used_deprecated_parameters:
- module.warn(
- f"'{param_name}' parameter is deprecated and will be removed in the version 2.0"
- )
-
- if module.params["addresses"]:
- for address in module.params["addresses"]:
- if not is_valid_network(address):
- module.fail_json(
- msg=f"'{address}' is not a valid address in CIDR notation"
- )
-
- sendp = get_se(module, fusion)
-
- if state == "present" and not sendp:
- module.fail_on_missing_params(["addresses"])
- if not (module.params["addresses"]):
- module.fail_json(
- msg="At least one entry in 'addresses' is required to create new storage endpoint"
- )
- create_se_old(module, fusion)
- elif state == "present" and sendp:
- update_se(module, fusion, sendp)
- elif state == "absent" and sendp:
- delete_se(module, fusion)
- else:
- # user uses new module interface
- if module.params["iscsi"] is not None:
- for endpoint in module.params["iscsi"]:
- address = endpoint["address"]
- if not is_valid_network(address):
- module.fail_json(
- msg=f"'{address}' is not a valid address in CIDR notation"
- )
- gateway = endpoint["gateway"]
- if not is_valid_address(gateway):
- module.fail_json(
- msg=f"'{gateway}' is not a valid IPv4 address notation"
- )
- if module.params["cbs_azure_iscsi"] is not None:
- for address in module.params["cbs_azure_iscsi"]["load_balancer_addresses"]:
- if not is_valid_address(address):
- module.fail_json(
- msg=f"'{address}' is not a valid IPv4 address notation"
- )
-
- sendp = get_se(module, fusion)
-
- if state == "present" and not sendp:
- if (
- module.params["iscsi"] is None
- and module.params["cbs_azure_iscsi"] is None
- ):
- module.fail_json(
- msg="either 'iscsi' or `cbs_azure_iscsi` parameter is required when creating storage endpoint"
- )
- create_se(module, fusion)
- elif state == "present" and sendp:
- update_se(module, fusion, sendp)
- elif state == "absent" and sendp:
- delete_se(module, fusion)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ss.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ss.py
deleted file mode 100644
index 4e6388249..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ss.py
+++ /dev/null
@@ -1,211 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_ss
-version_added: '1.0.0'
-short_description: Manage storage services in Pure Storage Fusion
-description:
-- Manage a storage services in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the storage service.
- type: str
- required: true
- state:
- description:
- - Define whether the storage service should exist or not.
- default: present
- choices: [ present, absent ]
- type: str
- display_name:
- description:
- - The human name of the storage service.
- - If not provided, defaults to I(name).
- type: str
- hardware_types:
- description:
- - Hardware types to which the storage service applies.
- type: list
- elements: str
- choices: [ flash-array-x, flash-array-c, flash-array-x-optane, flash-array-xl ]
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new storage service foo
- purestorage.fusion.fusion_ss:
- name: foo
- hardware_types:
- - flash-array-x
- - flash-array-x-optane
- display_name: "test class"
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Update storage service
- purestorage.fusion.fusion_ss:
- name: foo
- display_name: "main class"
- hardware_types:
- - flash-array-c
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete storage service
- purestorage.fusion.fusion_ss:
- name: foo
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils import getters
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-
-
-def get_ss(module, fusion):
- """Return Storage Service or None"""
- return getters.get_ss(module, fusion, storage_service_name=module.params["name"])
-
-
-def create_ss(module, fusion):
- """Create Storage Service"""
-
- ss_api_instance = purefusion.StorageServicesApi(fusion)
-
- changed = True
- id = None
- if not module.check_mode:
- if not module.params["display_name"]:
- display_name = module.params["name"]
- else:
- display_name = module.params["display_name"]
- s_service = purefusion.StorageServicePost(
- name=module.params["name"],
- display_name=display_name,
- hardware_types=module.params["hardware_types"],
- )
- op = ss_api_instance.create_storage_service(s_service)
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- module.exit_json(changed=changed, id=id)
-
-
-def delete_ss(module, fusion):
- """Delete Storage Service"""
-
- ss_api_instance = purefusion.StorageServicesApi(fusion)
-
- changed = True
- if not module.check_mode:
- op = ss_api_instance.delete_storage_service(
- storage_service_name=module.params["name"]
- )
- await_operation(fusion, op)
-
- module.exit_json(changed=changed)
-
-
-def update_ss(module, fusion, ss):
- """Update Storage Service"""
-
- ss_api_instance = purefusion.StorageServicesApi(fusion)
- patches = []
- if (
- module.params["display_name"]
- and module.params["display_name"] != ss.display_name
- ):
- patch = purefusion.StorageServicePatch(
- display_name=purefusion.NullableString(module.params["display_name"]),
- )
- patches.append(patch)
-
- id = None
- if not module.check_mode:
- for patch in patches:
- op = ss_api_instance.update_storage_service(
- patch,
- storage_service_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- changed = len(patches) != 0
-
- module.exit_json(changed=changed, id=ss.id)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- display_name=dict(type="str"),
- hardware_types=dict(
- type="list",
- elements="str",
- choices=[
- "flash-array-x",
- "flash-array-c",
- "flash-array-x-optane",
- "flash-array-xl",
- ],
- ),
- state=dict(type="str", default="present", choices=["present", "absent"]),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- s_service = get_ss(module, fusion)
-
- if not s_service and state == "present":
- module.fail_on_missing_params(["hardware_types"])
- create_ss(module, fusion)
- elif s_service and state == "present":
- update_ss(module, fusion, s_service)
- elif s_service and state == "absent":
- delete_ss(module, fusion)
- else:
- module.exit_json(changed=False)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_tenant.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_tenant.py
deleted file mode 100644
index 85224a6c5..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_tenant.py
+++ /dev/null
@@ -1,171 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_tenant
-version_added: '1.0.0'
-short_description: Manage tenants in Pure Storage Fusion
-description:
-- Create,delete or update a tenant in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the tenant.
- type: str
- required: true
- state:
- description:
- - Define whether the tenant should exist or not.
- default: present
- choices: [ present, absent ]
- type: str
- display_name:
- description:
- - The human name of the tenant.
- - If not provided, defaults to I(name).
- type: str
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new tenat foo
- purestorage.fusion.fusion_tenant:
- name: foo
- display_name: "tenant foo"
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete tenat foo
- purestorage.fusion.fusion_tenant:
- name: foo
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils import getters
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-
-
-def get_tenant(module, fusion):
- """Return Tenant or None"""
- return getters.get_tenant(module, fusion, tenant_name=module.params["name"])
-
-
-def create_tenant(module, fusion):
- """Create Tenant"""
-
- api_instance = purefusion.TenantsApi(fusion)
- changed = True
- id = None
- if not module.check_mode:
- if not module.params["display_name"]:
- display_name = module.params["name"]
- else:
- display_name = module.params["display_name"]
- tenant = purefusion.TenantPost(
- name=module.params["name"],
- display_name=display_name,
- )
- op = api_instance.create_tenant(tenant)
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- module.exit_json(changed=changed, id=id)
-
-
-def update_tenant(module, fusion, tenant):
- """Update Tenant settings"""
- changed = False
- api_instance = purefusion.TenantsApi(fusion)
-
- if (
- module.params["display_name"]
- and module.params["display_name"] != tenant.display_name
- ):
- changed = True
- if not module.check_mode:
- new_tenant = purefusion.TenantPatch(
- display_name=purefusion.NullableString(module.params["display_name"]),
- )
- op = api_instance.update_tenant(
- new_tenant,
- tenant_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- module.exit_json(changed=changed, id=tenant.id)
-
-
-def delete_tenant(module, fusion):
- """Delete Tenant"""
- changed = True
- api_instance = purefusion.TenantsApi(fusion)
- if not module.check_mode:
- op = api_instance.delete_tenant(tenant_name=module.params["name"])
- await_operation(fusion, op)
-
- module.exit_json(changed=changed)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- display_name=dict(type="str"),
- state=dict(type="str", default="present", choices=["present", "absent"]),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- tenant = get_tenant(module, fusion)
-
- if not tenant and state == "present":
- create_tenant(module, fusion)
- elif tenant and state == "present":
- update_tenant(module, fusion, tenant)
- elif tenant and state == "absent":
- delete_tenant(module, fusion)
- else:
- module.exit_json(changed=False)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_tn.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_tn.py
deleted file mode 100644
index 717b1e46f..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_tn.py
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_tn
-version_added: '1.0.0'
-deprecated:
- removed_at_date: "2023-07-26"
- why: Tenant Networks were removed as a concept in Pure Storage Fusion
- alternative: most of the functionality can be replicated using M(purestorage.fusion.fusion_se) and M(purestorage.fusion.fusion_nig)
-short_description: Manage tenant networks in Pure Storage Fusion
-description:
-- Create or delete tenant networks in Pure Storage Fusion.
-notes:
-- Supports C(check_mode).
-- Currently this only supports a single tenant subnet per tenant network.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-options:
- name:
- description:
- - The name of the tenant network.
- type: str
- display_name:
- description:
- - The human name of the tenant network.
- - If not provided, defaults to I(name).
- type: str
- state:
- description:
- - Define whether the tenant network should exist or not.
- type: str
- default: present
- choices: [ absent, present ]
- region:
- description:
- - The name of the region the availability zone is in
- type: str
- availability_zone:
- aliases: [ az ]
- description:
- - The name of the availability zone for the tenant network.
- type: str
- provider_subnets:
- description:
- - List of provider subnets to assign to the tenant networks subnet.
- type: list
- elements: str
- addresses:
- description:
- - List of IP addresses to be used in the subnet of the tenant network.
- - IP addresses must include a CIDR notation.
- - IPv4 and IPv6 are fully supported.
- type: list
- elements: str
- gateway:
- description:
- - Address of the subnet gateway.
- - Currently this must be provided.
- type: str
- mtu:
- description:
- - MTU setting for the subnet.
- default: 1500
- type: int
- prefix:
- description:
- - Network prefix in CIDR format.
- - This will be deprecated soon.
- type: str
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-# this module does nothing, thus no example is provided
-EXAMPLES = r"""
-"""
-
-RETURN = r"""
-"""
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str"),
- region=dict(type="str"),
- display_name=dict(type="str"),
- availability_zone=dict(type="str", aliases=["az"]),
- prefix=dict(type="str"),
- gateway=dict(type="str"),
- mtu=dict(type="int", default=1500),
- provider_subnets=dict(type="list", elements="str"),
- addresses=dict(type="list", elements="str"),
- state=dict(type="str", default="present", choices=["absent", "present"]),
- )
- )
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- module.warn(
- "This module is deprecated, doesn't work, and will be removed in the version 2.0."
- " Please, use purestorage.fusion.fusion_se and purestorage.fusion.fusion_nig instead."
- )
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ts.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ts.py
deleted file mode 100644
index ac60476bc..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ts.py
+++ /dev/null
@@ -1,189 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2022, Simon Dodsley (simon@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_ts
-version_added: '1.0.0'
-short_description: Manage tenant spaces in Pure Storage Fusion
-description:
-- Create, update or delete a tenant spaces in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the tenant space.
- type: str
- required: true
- display_name:
- description:
- - The human name of the tenant space.
- - If not provided, defaults to I(name).
- type: str
- state:
- description:
- - Define whether the tenant space should exist or not.
- type: str
- default: present
- choices: [ absent, present ]
- tenant:
- description:
- - The name of the tenant.
- type: str
- required: true
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new tenant space foo for tenant bar
- purestorage.fusion.fusion_ts:
- name: foo
- tenant: bar
- state: present
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete tenant space foo in tenant bar
- purestorage.fusion.fusion_ts:
- name: foo
- tenant: bar
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils import getters
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-
-
-def get_ts(module, fusion):
- """Tenant Space or None"""
- return getters.get_ts(module, fusion, tenant_space_name=module.params["name"])
-
-
-def create_ts(module, fusion):
- """Create Tenant Space"""
-
- ts_api_instance = purefusion.TenantSpacesApi(fusion)
-
- changed = True
- id = None
- if not module.check_mode:
- if not module.params["display_name"]:
- display_name = module.params["name"]
- else:
- display_name = module.params["display_name"]
- tspace = purefusion.TenantSpacePost(
- name=module.params["name"],
- display_name=display_name,
- )
- op = ts_api_instance.create_tenant_space(
- tspace,
- tenant_name=module.params["tenant"],
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
-
- module.exit_json(changed=changed, id=id)
-
-
-def update_ts(module, fusion, ts):
- """Update Tenant Space"""
-
- ts_api_instance = purefusion.TenantSpacesApi(fusion)
- patches = []
- if (
- module.params["display_name"]
- and module.params["display_name"] != ts.display_name
- ):
- patch = purefusion.TenantSpacePatch(
- display_name=purefusion.NullableString(module.params["display_name"]),
- )
- patches.append(patch)
-
- if not module.check_mode:
- for patch in patches:
- op = ts_api_instance.update_tenant_space(
- patch,
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- changed = len(patches) != 0
-
- module.exit_json(changed=changed, id=ts.id)
-
-
-def delete_ts(module, fusion):
- """Delete Tenant Space"""
- changed = True
- ts_api_instance = purefusion.TenantSpacesApi(fusion)
- if not module.check_mode:
- op = ts_api_instance.delete_tenant_space(
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["name"],
- )
- await_operation(fusion, op)
-
- module.exit_json(changed=changed)
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- display_name=dict(type="str"),
- tenant=dict(type="str", required=True),
- state=dict(type="str", default="present", choices=["absent", "present"]),
- )
- )
-
- module = AnsibleModule(argument_spec, supports_check_mode=True)
- fusion = setup_fusion(module)
-
- state = module.params["state"]
- tspace = get_ts(module, fusion)
-
- if state == "present" and not tspace:
- create_ts(module, fusion)
- elif state == "present" and tspace:
- update_ts(module, fusion, tspace)
- elif state == "absent" and tspace:
- delete_ts(module, fusion)
-
- module.exit_json(changed=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_volume.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_volume.py
deleted file mode 100644
index 38dee8650..000000000
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_volume.py
+++ /dev/null
@@ -1,559 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2023, Simon Dodsley (simon@purestorage.com), Jan Kodera (jkodera@purestorage.com)
-# GNU General Public License v3.0+ (see COPYING.GPLv3 or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: fusion_volume
-version_added: '1.0.0'
-short_description: Manage volumes in Pure Storage Fusion
-description:
-- Create, update or delete a volume in Pure Storage Fusion.
-author:
-- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
-notes:
-- Supports C(check mode).
-options:
- name:
- description:
- - The name of the volume.
- type: str
- required: true
- display_name:
- description:
- - The human name of the volume.
- - If not provided, defaults to I(name).
- type: str
- state:
- description:
- - Define whether the volume should exist or not.
- type: str
- default: present
- choices: [ absent, present ]
- tenant:
- description:
- - The name of the tenant.
- type: str
- required: true
- tenant_space:
- description:
- - The name of the tenant space.
- type: str
- required: true
- eradicate:
- description:
- - "Wipes the volume instead of a soft delete if true. Must be used with `state: absent`."
- type: bool
- default: false
- size:
- description:
- - Volume size in M, G, T or P units.
- type: str
- storage_class:
- description:
- - The name of the storage class.
- type: str
- placement_group:
- description:
- - The name of the placement group.
- type: str
- protection_policy:
- description:
- - The name of the protection policy.
- type: str
- host_access_policies:
- description:
- - 'A list of host access policies to connect the volume to.
- To clear, assign empty list: host_access_policies: []'
- type: list
- elements: str
- source_volume:
- description:
- - The source volume name. It must live within the same tenant space.
- Cannot be used together with `source_snapshot` or `source_volume_snapshot`.
- type: str
- source_snapshot:
- description:
- - The source snapshot name. It must live within the same tenant space.
- Cannot be used together with `source_volume`.
- type: str
- source_volume_snapshot:
- description:
- - The source volume snapshot name. It must live within the same tenant space.
- Cannot be used together with `source_volume`.
- type: str
- rename:
- description:
- - New name for volume.
- type: str
-extends_documentation_fragment:
-- purestorage.fusion.purestorage.fusion
-"""
-
-EXAMPLES = r"""
-- name: Create new volume named foo in storage_class fred
- purestorage.fusion.fusion_volume:
- name: foo
- storage_class: fred
- placement_group: pg
- size: 1T
- tenant: test
- tenant_space: space_1
- state: present
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Create new volume based on a volume from the same tenant space
- purestorage.fusion.fusion_volume:
- name: foo
- storage_class: fred
- placement_group: pg
- tenant: test
- tenant_space: space_1
- state: present
- source_volume: "original_volume_name"
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Create new volume based on a volume snapshot from the same tenant space
- purestorage.fusion.fusion_volume:
- name: foo
- storage_class: fred
- placement_group: pg
- tenant: test
- tenant_space: space_1
- state: present
- source_snapshot: "snap"
- source_volume_snapshot: "vol_snap"
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Extend the size of an existing volume named foo
- purestorage.fusion.fusion_volume:
- name: foo
- size: 2T
- tenant: test
- tenant_space: space_1
- state: present
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-
-- name: Delete volume named foo
- purestorage.fusion.fusion_volume:
- name: foo
- tenant: test
- tenant_space: space_1
- state: absent
- issuer_id: key_name
- private_key_file: "az-admin-private-key.pem"
-"""
-
-RETURN = r"""
-"""
-
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
- setup_fusion,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.parsing import (
- parse_number_with_metric_suffix,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-from ansible.module_utils.basic import AnsibleModule
-
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-
-def get_volume(module, fusion):
- """Return Volume or None"""
- volume_api_instance = purefusion.VolumesApi(fusion)
- try:
- return volume_api_instance.get_volume(
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["tenant_space"],
- volume_name=module.params["name"],
- )
- except purefusion.rest.ApiException:
- return None
-
-
-def get_wanted_haps(module):
- """Return set of host access policies to assign"""
- if not module.params["host_access_policies"]:
- return set()
- # looks like yaml parsing can leave in some spaces if coma-delimited .so strip() the names
- return set([hap.strip() for hap in module.params["host_access_policies"]])
-
-
-def extract_current_haps(volume):
- """Return set of host access policies that volume currently has"""
- if not volume.host_access_policies:
- return set()
- return set([hap.name for hap in volume.host_access_policies])
-
-
-def create_volume(module, fusion):
- """Create Volume"""
- id = None
- if not module.check_mode:
- display_name = module.params["display_name"] or module.params["name"]
- volume_api_instance = purefusion.VolumesApi(fusion)
- source_link = get_source_link_from_parameters(module.params)
- volume = purefusion.VolumePost(
- size=None # when cloning a volume, size is not required
- if source_link
- else parse_number_with_metric_suffix(module, module.params["size"]),
- storage_class=module.params["storage_class"],
- placement_group=module.params["placement_group"],
- name=module.params["name"],
- display_name=display_name,
- protection_policy=module.params["protection_policy"],
- source_link=source_link,
- )
- op = volume_api_instance.create_volume(
- volume,
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["tenant_space"],
- )
- res_op = await_operation(fusion, op)
- id = res_op.result.resource.id
- return True, id
-
-
-def update_host_access_policies(module, current, patches):
- wanted = module.params
- # 'wanted[...] is not None' to differentiate between empty list and no list
- if wanted["host_access_policies"] is not None:
- current_haps = extract_current_haps(current)
- wanted_haps = get_wanted_haps(module)
- if wanted_haps != current_haps:
- patch = purefusion.VolumePatch(
- host_access_policies=purefusion.NullableString(",".join(wanted_haps))
- )
- patches.append(patch)
-
-
-def update_destroyed(module, current, patches):
- wanted = module.params
- destroyed = wanted["state"] != "present"
- if destroyed != current.destroyed:
- patch = purefusion.VolumePatch(destroyed=purefusion.NullableBoolean(destroyed))
- patches.append(patch)
- if destroyed and not module.params["eradicate"]:
- module.warn(
- (
- "Volume '{0}' is being soft deleted to prevent data loss, "
- "if you want to wipe it immediately to reclaim used space, add 'eradicate: true'"
- ).format(current.name)
- )
-
-
-def update_display_name(module, current, patches):
- wanted = module.params
- if wanted["display_name"] and wanted["display_name"] != current.display_name:
- patch = purefusion.VolumePatch(
- display_name=purefusion.NullableString(wanted["display_name"])
- )
- patches.append(patch)
-
-
-def update_storage_class(module, current, patches):
- wanted = module.params
- if (
- wanted["storage_class"]
- and wanted["storage_class"] != current.storage_class.name
- ):
- patch = purefusion.VolumePatch(
- storage_class=purefusion.NullableString(wanted["storage_class"])
- )
- patches.append(patch)
-
-
-def update_placement_group(module, current, patches):
- wanted = module.params
- if (
- wanted["placement_group"]
- and wanted["placement_group"] != current.placement_group.name
- ):
- patch = purefusion.VolumePatch(
- placement_group=purefusion.NullableString(wanted["placement_group"])
- )
- patches.append(patch)
-
-
-def update_size(module, current, patches):
- wanted = module.params
- if wanted["size"]:
- wanted_size = parse_number_with_metric_suffix(module, wanted["size"])
- if wanted_size != current.size:
- patch = purefusion.VolumePatch(size=purefusion.NullableSize(wanted_size))
- patches.append(patch)
-
-
-def update_protection_policy(module, current, patches):
- wanted = module.params
- current_policy = current.protection_policy.name if current.protection_policy else ""
- if (
- wanted["protection_policy"] is not None
- and wanted["protection_policy"] != current_policy
- ):
- patch = purefusion.VolumePatch(
- protection_policy=purefusion.NullableString(wanted["protection_policy"])
- )
- patches.append(patch)
-
-
-def update_source_link(module, fusion, current, patches):
- source_link = get_source_link_from_parameters(module.params)
- if source_link is not None and (
- current.source is None or current.source.self_link != source_link
- ):
- patch = purefusion.VolumePatch(
- source_link=purefusion.NullableString(source_link)
- )
- patches.append(patch)
-
-
-def apply_patches(module, fusion, patches):
- volume_api_instance = purefusion.VolumesApi(fusion)
- for patch in patches:
- op = volume_api_instance.update_volume(
- patch,
- volume_name=module.params["name"],
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["tenant_space"],
- )
- await_operation(fusion, op)
-
-
-def update_volume(module, fusion):
- """Update Volume size, placement group, protection policy, storage class, HAPs"""
- current = get_volume(module, fusion)
- patches = []
-
- if not current:
- # cannot update nonexistent volume
- # Note for check mode: the reasons this codepath is ran in check mode
- # is to catch any argument errors and to compute 'changed'. Basically
- # all argument checks are kept in validate_arguments() to filter the
- # first part. The second part MAY diverge flow from the real run here if
- # create_volume() created the volume and update was then run to update
- # its properties. HOWEVER we don't really care in that case because
- # create_volume() already sets 'changed' to true, so any 'changed'
- # result from update_volume() would not change it.
- return False
-
- # volumes with 'destroyed' flag are kinda special because we can't change
- # most of their properties while in this state, so we need to set it last
- # and unset it first if changed, respectively
- if module.params["state"] == "present":
- update_destroyed(module, current, patches)
- update_size(module, current, patches)
- update_protection_policy(module, current, patches)
- update_display_name(module, current, patches)
- update_storage_class(module, current, patches)
- update_placement_group(module, current, patches)
- update_host_access_policies(module, current, patches)
- update_source_link(module, fusion, current, patches)
- elif module.params["state"] == "absent" and not current.destroyed:
- update_size(module, current, patches)
- update_protection_policy(module, current, patches)
- update_display_name(module, current, patches)
- update_storage_class(module, current, patches)
- update_placement_group(module, current, patches)
- update_host_access_policies(module, current, patches)
- update_source_link(module, fusion, current, patches)
- update_destroyed(module, current, patches)
-
- if not module.check_mode:
- apply_patches(module, fusion, patches)
-
- changed = len(patches) != 0
- return changed
-
-
-def eradicate_volume(module, fusion):
- """Eradicate Volume"""
- current = get_volume(module, fusion)
- if module.check_mode:
- return current or module.params["state"] == "present"
- if not current:
- return False
-
- # update_volume() should be called before eradicate=True and it should
- # ensure the volume is destroyed and HAPs are unassigned
- if not current.destroyed or current.host_access_policies:
- module.fail_json(
- msg="BUG: inconsistent state, eradicate_volume() cannot be called with current.destroyed=False or any host_access_policies"
- )
-
- volume_api_instance = purefusion.VolumesApi(fusion)
- op = volume_api_instance.delete_volume(
- volume_name=module.params["name"],
- tenant_name=module.params["tenant"],
- tenant_space_name=module.params["tenant_space"],
- )
- await_operation(fusion, op)
-
- return True
-
-
-def get_source_link_from_parameters(params):
- tenant = params["tenant"]
- tenant_space = params["tenant_space"]
- volume = params["source_volume"]
- snapshot = params["source_snapshot"]
- volume_snapshot = params["source_volume_snapshot"]
- if (
- tenant is None or tenant_space is None
- ): # should not happen as those parameters are always required by the ansible module
- return None
- if volume is not None:
- return f"/tenants/{tenant}/tenant-spaces/{tenant_space}/volumes/{volume}"
- if snapshot is not None and volume_snapshot is not None:
- return f"/tenants/{tenant}/tenant-spaces/{tenant_space}/snapshots/{snapshot}/volume-snapshots/{volume_snapshot}"
- return None
-
-
-def validate_arguments(module, volume):
- """Validates most argument conditions and possible unacceptable argument combinations"""
- state = module.params["state"]
-
- if state == "present" and not volume:
- module.fail_on_missing_params(["placement_group", "storage_class"])
-
- if (
- module.params["size"] is None
- and module.params["source_volume"] is None
- and module.params["source_snapshot"] is None
- ):
- module.fail_json(
- msg="Either `size`, `source_volume` or `source_snapshot` parameter is required when creating a volume."
- )
-
- if module.params["state"] == "absent" and (
- module.params["host_access_policies"]
- or (
- module.params["host_access_policies"] is None
- and volume
- and volume.host_access_policies
- )
- ):
- module.fail_json(
- msg=(
- "Volume must have no host access policies when destroyed, either revert the delete "
- "by setting 'state: present' or remove all HAPs by 'host_access_policies: []'"
- )
- )
-
- if state == "present" and module.params["eradicate"]:
- module.fail_json(
- msg="'eradicate: true' cannot be used together with 'state: present'"
- )
-
- if module.params["size"] is not None:
- size = parse_number_with_metric_suffix(module, module.params["size"])
- if size < 1048576 or size > 4503599627370496: # 1MB to 4PB
- module.fail_json(
- msg="Size is not within the required range, size must be between 1MB and 4PB"
- )
-
-
-def main():
- """Main code"""
- argument_spec = fusion_argument_spec()
- deprecated_hosts = dict(
- name="hosts", date="2023-07-26", collection_name="purefusion.fusion"
- )
- argument_spec.update(
- dict(
- name=dict(type="str", required=True),
- display_name=dict(type="str"),
- rename=dict(
- type="str",
- removed_at_date="2023-07-26",
- removed_from_collection="purestorage.fusion",
- ),
- tenant=dict(type="str", required=True),
- tenant_space=dict(type="str", required=True),
- placement_group=dict(type="str"),
- storage_class=dict(type="str"),
- protection_policy=dict(type="str"),
- host_access_policies=dict(
- type="list", elements="str", deprecated_aliases=[deprecated_hosts]
- ),
- eradicate=dict(type="bool", default=False),
- state=dict(type="str", default="present", choices=["absent", "present"]),
- size=dict(type="str"),
- source_volume=dict(type="str"),
- source_snapshot=dict(type="str"),
- source_volume_snapshot=dict(type="str"),
- )
- )
-
- required_by = {
- "placement_group": "storage_class",
- }
-
- mutually_exclusive = [
- # a new volume cannot be based on a volume and a snapshot at the same time
- # also, when cloning a volume, size of original volume is used
- ("source_volume", "source_snapshot", "size"),
- ]
-
- required_together = [
- # when creating a volume from snapshot, we need to know both snapshot name and snapshot volume name
- ("source_snapshot", "source_volume_snapshot"),
- ]
-
- module = AnsibleModule(
- argument_spec,
- required_by=required_by,
- mutually_exclusive=mutually_exclusive,
- required_together=required_together,
- supports_check_mode=True,
- )
- fusion = setup_fusion(module)
-
- state = module.params["state"]
-
- volume = get_volume(module, fusion)
-
- validate_arguments(module, volume)
-
- if state == "absent" and not volume:
- module.exit_json(changed=False)
-
- changed = False
- id = None
- if volume is not None:
- id = volume.id
- if state == "present" and not volume:
- changed, id = create_volume(module, fusion)
- # volume might exist even if soft-deleted, so we still have to update it
- changed = changed | update_volume(module, fusion)
- if module.params["eradicate"]:
- changed = changed | eradicate_volume(module, fusion)
- module.exit_json(changed=changed)
-
- if id is not None:
- module.exit_json(changed=changed, id=id)
-
- module.exit_json(changed=changed)
-
-
-if __name__ == "__main__":
- main()