summaryrefslogtreecommitdiffstats
path: root/ansible_collections/infinidat/infinibox/plugins/module_utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:35 +0000
commit7fec0b69a082aaeec72fee0612766aa42f6b1b4d (patch)
treeefb569b86ca4da888717f5433e757145fa322e08 /ansible_collections/infinidat/infinibox/plugins/module_utils
parentReleasing progress-linux version 7.7.0+dfsg-3~progress7.99u1. (diff)
downloadansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.tar.xz
ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.zip
Merging upstream version 9.4.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/infinidat/infinibox/plugins/module_utils')
-rw-r--r--ansible_collections/infinidat/infinibox/plugins/module_utils/infinibox.py228
1 files changed, 179 insertions, 49 deletions
diff --git a/ansible_collections/infinidat/infinibox/plugins/module_utils/infinibox.py b/ansible_collections/infinidat/infinibox/plugins/module_utils/infinibox.py
index 31df73d04..24f3aa9fb 100644
--- a/ansible_collections/infinidat/infinibox/plugins/module_utils/infinibox.py
+++ b/ansible_collections/infinidat/infinibox/plugins/module_utils/infinibox.py
@@ -1,16 +1,21 @@
# -*- coding: utf-8 -*-
-# Copyright: (c) 2022, Infinidat <info@infinidat.com>
+# Copyright: (c) 2024, Infinidat <info@infinidat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# pylint:
+# disable=use-list-literal,use-dict-literal,line-too-long,wrong-import-position,broad-exception-caught,invalid-name
+
+""" Infinidat utilities """
+
from __future__ import (absolute_import, division, print_function)
+
__metaclass__ = type
-from ansible.module_utils.six import raise_from
-try:
- import ansible.module_utils.errors
-except (ImportError, ModuleNotFoundError):
- import errors # Used during "make dev-hack-module-[present, stat, absent]"
+# try:
+# import ansible.module_utils.errors
+# except (ImportError, ModuleNotFoundError):
+# import errors # Used during "make dev-hack-module-[present, stat, absent]"
try:
from infinisdk import InfiniBox, core
@@ -22,13 +27,32 @@ else:
HAS_INFINISDK = True
INFINISDK_IMPORT_ERROR = None
+HAS_ARROW = True
+try:
+ import arrow
+except ImportError:
+ HAS_ARROW = False
+except Exception:
+ HAS_INFINISDK = False
+
from functools import wraps
from os import environ
from os import path
from datetime import datetime
+HAS_URLLIB3 = True
+try:
+ import urllib3
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+except ImportError:
+ HAS_URLLIB3 = False
+
-def unixMillisecondsToDate(unix_ms):
+INFINIBOX_SYSTEM = None
+
+
+def unixMillisecondsToDate(unix_ms): # pylint: disable=invalid-name
+ """ Convert unix time with ms to a datetime UTC time """
return (datetime.utcfromtimestamp(unix_ms / 1000.), 'UTC')
@@ -39,12 +63,13 @@ def api_wrapper(func):
module = args[0]
try:
return func(*args, **kwargs)
- except core.exceptions.APICommandException as e:
- module.fail_json(msg=e.message)
- except core.exceptions.SystemNotFoundException as e:
- module.fail_json(msg=e.message)
- except Exception:
- raise
+ except core.exceptions.SystemNotFoundException as err:
+ module.fail_json(msg=str(err))
+ except core.exceptions.APICommandException as err:
+ module.fail_json(msg=str(err))
+ except Exception as err:
+ module.fail_json(msg=str(err))
+ return None # Should never get to this line but it quiets pylint inconsistent-return-statements
return __wrapper
@@ -74,28 +99,37 @@ def merge_two_dicts(dict1, dict2):
@api_wrapper
def get_system(module):
- """Return System Object or Fail"""
- box = module.params['system']
- user = module.params.get('user', None)
- password = module.params.get('password', None)
-
- if user and password:
- system = InfiniBox(box, auth=(user, password), use_ssl=True)
- elif environ.get('INFINIBOX_USER') and environ.get('INFINIBOX_PASSWORD'):
- system = InfiniBox(box,
- auth=(environ.get('INFINIBOX_USER'),
- environ.get('INFINIBOX_PASSWORD')),
- use_ssl=True)
- elif path.isfile(path.expanduser('~') + '/.infinidat/infinisdk.ini'):
- system = InfiniBox(box, use_ssl=True)
- else:
- module.fail_json(msg="You must set INFINIBOX_USER and INFINIBOX_PASSWORD environment variables or set username/password module arguments")
+ """
+ Return System Object if it does not exist or Fail.
+ Use a global system Infinibox object so that there will only be one
+ system session used for this module instance.
+ Enables execute_state() to log out of the only session properly.
+ """
+ global INFINIBOX_SYSTEM # pylint: disable=global-statement
+
+ if not INFINIBOX_SYSTEM:
+ # Create system and login
+ box = module.params['system']
+ user = module.params.get('user', None)
+ password = module.params.get('password', None)
+ if user and password:
+ INFINIBOX_SYSTEM = InfiniBox(box, auth=(user, password), use_ssl=True)
+ elif environ.get('INFINIBOX_USER') and environ.get('INFINIBOX_PASSWORD'):
+ INFINIBOX_SYSTEM = InfiniBox(box,
+ auth=(environ.get('INFINIBOX_USER'),
+ environ.get('INFINIBOX_PASSWORD')),
+ use_ssl=True)
+ elif path.isfile(path.expanduser('~') + '/.infinidat/infinisdk.ini'):
+ INFINIBOX_SYSTEM = InfiniBox(box, use_ssl=True)
+ else:
+ module.fail_json(msg="You must set INFINIBOX_USER and INFINIBOX_PASSWORD environment variables or set username/password module arguments")
- try:
- system.login()
- except Exception:
- module.fail_json(msg="Infinibox authentication failed. Check your credentials")
- return system
+ try:
+ INFINIBOX_SYSTEM.login()
+ except Exception:
+ module.fail_json(msg="Infinibox authentication failed. Check your credentials")
+
+ return INFINIBOX_SYSTEM
@api_wrapper
@@ -108,7 +142,10 @@ def get_pool(module, system):
try:
name = module.params['pool']
except KeyError:
- name = module.params['name']
+ try:
+ name = module.params['name']
+ except KeyError:
+ name = module.params['object_name'] # For metadata
return system.pools.get(name=name)
except Exception:
return None
@@ -121,7 +158,10 @@ def get_filesystem(module, system):
try:
filesystem = system.filesystems.get(name=module.params['filesystem'])
except KeyError:
- filesystem = system.filesystems.get(name=module.params['name'])
+ try:
+ filesystem = system.filesystems.get(name=module.params['name'])
+ except KeyError:
+ filesystem = system.filesystems.get(name=module.params['object_name'])
return filesystem
except Exception:
return None
@@ -137,7 +177,7 @@ def get_export(module, system):
export_name = module.params['name']
export = system.exports.get(export_path=export_name)
- except ObjectNotFound as err:
+ except ObjectNotFound:
return None
return export
@@ -150,7 +190,10 @@ def get_volume(module, system):
try:
volume = system.volumes.get(name=module.params['name'])
except KeyError:
- volume = system.volumes.get(name=module.params['volume'])
+ try:
+ volume = system.volumes.get(name=module.params['volume'])
+ except KeyError:
+ volume = system.volumes.get(name=module.params['object_name']) # Used by metadata module
return volume
except Exception:
return None
@@ -167,16 +210,23 @@ def get_net_space(module, system):
@api_wrapper
-def get_vol_sn(module, system):
- """Return Volume or None"""
+def get_vol_by_sn(module, system):
+ """Return volume that matches the serial or None"""
try:
- try:
- volume = system.volumes.get(serial=module.params['serial'])
- except KeyError:
- return None
- return volume
+ volume = system.volumes.get(serial=module.params['serial'])
except Exception:
return None
+ return volume
+
+
+@api_wrapper
+def get_fs_by_sn(module, system):
+ """Return filesystem that matches the serial or None"""
+ try:
+ filesystem = system.filesystems.get(serial=module.params['serial'])
+ except Exception:
+ return None
+ return filesystem
@api_wrapper
@@ -189,7 +239,10 @@ def get_host(module, system):
try:
host_param = module.params['name']
except KeyError:
- host_param = module.params['host']
+ try:
+ host_param = module.params['host']
+ except KeyError:
+ host_param = module.params['object_name'] # For metadata
if a_host_name == host_param:
host = a_host
@@ -208,7 +261,10 @@ def get_cluster(module, system):
try:
cluster_param = module.params['name']
except KeyError:
- cluster_param = module.params['cluster']
+ try:
+ cluster_param = module.params['cluster']
+ except KeyError:
+ cluster_param = module.params['object_name'] # For metadata
if a_cluster_name == cluster_param:
cluster = a_cluster
@@ -217,12 +273,86 @@ def get_cluster(module, system):
@api_wrapper
-def get_user(module, system):
+def get_user(module, system, user_name_to_find=None):
"""Find a user by the user_name specified in the module"""
user = None
- user_name = module.params['user_name']
+ if not user_name_to_find:
+ user_name = module.params['user_name']
+ else:
+ user_name = user_name_to_find
try:
user = system.users.get(name=user_name)
except ObjectNotFound:
pass
return user
+
+
+def check_snapshot_lock_options(module):
+ """
+ Check if specified options are feasible for a snapshot.
+
+ Prevent very long lock times.
+ max_delta_minutes limits locks to 30 days (43200 minutes).
+
+ This functionality is broken out from manage_snapshot_locks() to allow
+ it to be called by create_snapshot() before the snapshot is actually
+ created.
+ """
+ snapshot_lock_expires_at = module.params["snapshot_lock_expires_at"]
+
+ if snapshot_lock_expires_at: # Then user has specified wish to lock snap
+ lock_expires_at = arrow.get(snapshot_lock_expires_at)
+
+ # Check for lock in the past
+ now = arrow.utcnow()
+ if lock_expires_at <= now:
+ msg = "Cannot lock snapshot with a snapshot_lock_expires_at "
+ msg += f"of '{snapshot_lock_expires_at}' from the past"
+ module.fail_json(msg=msg)
+
+ # Check for lock later than max lock, i.e. too far in future.
+ max_delta_minutes = 43200 # 30 days in minutes
+ max_lock_expires_at = now.shift(minutes=max_delta_minutes)
+ if lock_expires_at >= max_lock_expires_at:
+ msg = f"snapshot_lock_expires_at exceeds {max_delta_minutes // 24 // 60} days in the future"
+ module.fail_json(msg=msg)
+
+
+def manage_snapshot_locks(module, snapshot):
+ """
+ Manage the locking of a snapshot. Check for bad lock times.
+ See check_snapshot_lock_options() which has additional checks.
+ """
+ snapshot_lock_expires_at = module.params["snapshot_lock_expires_at"]
+ snap_is_locked = snapshot.get_lock_state() == "LOCKED"
+ current_lock_expires_at = snapshot.get_lock_expires_at()
+ changed = False
+
+ check_snapshot_lock_options(module)
+
+ if snapshot_lock_expires_at: # Then user has specified wish to lock snap
+ lock_expires_at = arrow.get(snapshot_lock_expires_at)
+ if snap_is_locked and lock_expires_at < current_lock_expires_at:
+ # Lock earlier than current lock
+ msg = f"snapshot_lock_expires_at '{lock_expires_at}' preceeds the current lock time of '{current_lock_expires_at}'"
+ module.fail_json(msg=msg)
+ elif snap_is_locked and lock_expires_at == current_lock_expires_at:
+ # Lock already set to correct time
+ pass
+ else:
+ # Set lock
+ if not module.check_mode:
+ snapshot.update_lock_expires_at(lock_expires_at)
+ changed = True
+ return changed
+
+
+def catch_failed_module_utils_imports(module):
+ msg = ""
+ if not HAS_ARROW:
+ msg += "Failed to import arrow module. "
+ if not HAS_INFINISDK:
+ msg += "Failed to import infinisdk module. "
+ if not HAS_URLLIB3:
+ msg += "Failed to import urllib3 module. "
+ module.fail_json(msg=msg)