From 6ab3d76696e6dda36aa5499859a1aea9e8ca0478 Mon Sep 17 00:00:00 2001
From: Jakob Meng <code@jakobmeng.de>
Date: Thu, 29 Sep 2022 20:43:18 +0200
Subject: [PATCH] Ensure openstacksdk compatibility in inventory plugin

Story: 2010337
Task: 46470

Change-Id: Ieb624b76627b5127d7a6c4d95233bbd5c2f16182
(cherry picked from commit e8bba38e2e78b0352f4ce5a93b8ebb43ce7714ed)
---
 plugins/inventory/openstack.py    |  10 +++
 plugins/module_utils/openstack.py | 102 ++++++++++++++----------------
 2 files changed, 58 insertions(+), 54 deletions(-)

diff --git a/plugins/inventory/openstack.py b/plugins/inventory/openstack.py
index b7f8920..86b271a 100644
--- a/plugins/inventory/openstack.py
+++ b/plugins/inventory/openstack.py
@@ -136,6 +136,9 @@ import logging
 from ansible.errors import AnsibleParserError
 from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
 from ansible.utils.display import Display
+from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
+    ensure_compatibility
+)
 
 display = Display()
 os_logger = logging.getLogger("openstack")
@@ -177,6 +180,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
         elif not HAS_SDK:
             msg = "openstacksdk is required for the OpenStack inventory plugin. OpenStack inventory sources will be skipped."
 
+        if not msg:
+            try:
+                ensure_compatibility(sdk.version.__version__)
+            except ImportError as e:
+                msg = ("Incompatible openstacksdk library found: {error}."
+                       .format(error=str(e)))
+
         if msg:
             display.vvvv(msg)
             raise AnsibleParserError(msg)
diff --git a/plugins/module_utils/openstack.py b/plugins/module_utils/openstack.py
index f116ca4..c7b4cae 100644
--- a/plugins/module_utils/openstack.py
+++ b/plugins/module_utils/openstack.py
@@ -55,6 +55,39 @@ MINIMUM_SDK_VERSION = '0.101.0'
 MAXIMUM_SDK_VERSION = None
 
 
+def ensure_compatibility(version, min_version=None, max_version=None):
+    """ Raises ImportError if the specified version does not
+        meet the minimum and maximum version requirements"""
+
+    if min_version and MINIMUM_SDK_VERSION:
+        min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
+                          StrictVersion(min_version))
+    elif MINIMUM_SDK_VERSION:
+        min_version = StrictVersion(MINIMUM_SDK_VERSION)
+
+    if max_version and MAXIMUM_SDK_VERSION:
+        max_version = min(StrictVersion(MAXIMUM_SDK_VERSION),
+                          StrictVersion(max_version))
+    elif MAXIMUM_SDK_VERSION:
+        max_version = StrictVersion(MAXIMUM_SDK_VERSION)
+
+    if min_version and StrictVersion(version) < min_version:
+        raise ImportError(
+            "Version MUST be >={min_version} and <={max_version}, but"
+            " {version} is smaller than minimum version {min_version}"
+            .format(version=version,
+                    min_version=min_version,
+                    max_version=max_version))
+
+    if max_version and StrictVersion(version) > max_version:
+        raise ImportError(
+            "Version MUST be >={min_version} and <={max_version}, but"
+            " {version} is larger than maximum version {max_version}"
+            .format(version=version,
+                    min_version=min_version,
+                    max_version=max_version))
+
+
 def openstack_argument_spec():
     # DEPRECATED: This argument spec is only used for the deprecated old
     # OpenStack modules. It turns out that modern OpenStack auth is WAY
@@ -129,33 +162,16 @@ def openstack_cloud_from_module(module, min_version=None, max_version=None):
     try:
         # Due to the name shadowing we should import other way
         sdk = importlib.import_module('openstack')
-        sdk_version = importlib.import_module('openstack.version')
     except ImportError:
         module.fail_json(msg='openstacksdk is required for this module')
 
-    if min_version and MINIMUM_SDK_VERSION:
-        min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
-                          StrictVersion(min_version))
-    elif MINIMUM_SDK_VERSION:
-        min_version = StrictVersion(MINIMUM_SDK_VERSION)
-
-    if max_version and MAXIMUM_SDK_VERSION:
-        max_version = min(StrictVersion(MAXIMUM_SDK_VERSION),
-                          StrictVersion(max_version))
-    elif MAXIMUM_SDK_VERSION:
-        max_version = StrictVersion(MAXIMUM_SDK_VERSION)
-
-    if min_version and StrictVersion(sdk_version.__version__) < min_version:
+    try:
+        ensure_compatibility(sdk.version.__version__,
+                             min_version, max_version)
+    except ImportError as e:
         module.fail_json(
-            msg="To utilize this module, the installed version of "
-                "the openstacksdk library MUST be >={min_version}.".format(
-                    min_version=min_version))
-
-    if max_version and StrictVersion(sdk_version.__version__) > max_version:
-        module.fail_json(
-            msg="To utilize this module, the installed version of "
-                "the openstacksdk library MUST be <={max_version}.".format(
-                    max_version=max_version))
+            msg="Incompatible openstacksdk library found: {error}."
+                .format(error=str(e)))
 
     cloud_config = module.params.pop('cloud', None)
     try:
@@ -298,40 +314,18 @@ class OpenStackModule:
         try:
             # Due to the name shadowing we should import other way
             sdk = importlib.import_module('openstack')
-            sdk_version_lib = importlib.import_module('openstack.version')
-            self.sdk_version = sdk_version_lib.__version__
+            self.sdk_version = sdk.version.__version__
         except ImportError:
             self.fail_json(msg='openstacksdk is required for this module')
 
-        # Fail if the available SDK version doesn't meet the minimum
-        # and maximum version requirements
-        if self.module_min_sdk_version and MINIMUM_SDK_VERSION:
-            min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
-                              StrictVersion(self.module_min_sdk_version))
-        elif MINIMUM_SDK_VERSION:
-            min_version = StrictVersion(MINIMUM_SDK_VERSION)
-        else:
-            min_version = None
-
-        if self.module_max_sdk_version and MAXIMUM_SDK_VERSION:
-            max_version = min(StrictVersion(MAXIMUM_SDK_VERSION),
-                              StrictVersion(self.module_max_sdk_version))
-        elif MAXIMUM_SDK_VERSION:
-            max_version = StrictVersion(MAXIMUM_SDK_VERSION)
-        else:
-            max_version = None
-
-        if min_version and StrictVersion(self.sdk_version) < min_version:
-            self.fail(
-                msg="To utilize this module, the installed version of "
-                "the openstacksdk library MUST be >={min_version}.".format(
-                    min_version=min_version))
-
-        if max_version and StrictVersion(self.sdk_version) > max_version:
-            self.fail(
-                msg="To utilize this module, the installed version of "
-                "the openstacksdk library MUST be <={max_version}.".format(
-                    max_version=max_version))
+        try:
+            ensure_compatibility(self.sdk_version,
+                                 self.module_min_sdk_version,
+                                 self.module_max_sdk_version)
+        except ImportError as e:
+            self.fail_json(
+                msg="Incompatible openstacksdk library found: {error}."
+                    .format(error=str(e)))
 
         # Fail if there are set unsupported for this version parameters
         # New parameters should NOT use 'default' but rely on SDK defaults
-- 
GitLab