diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:35 +0000 |
commit | 7fec0b69a082aaeec72fee0612766aa42f6b1b4d (patch) | |
tree | efb569b86ca4da888717f5433e757145fa322e08 /ansible_collections/infinidat/infinibox/plugins/module_utils | |
parent | Releasing progress-linux version 7.7.0+dfsg-3~progress7.99u1. (diff) | |
download | ansible-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.py | 228 |
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) |