diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py
index 82f48e85a21db7704e0e236eb7ec30f59c438a27..b6ae7a0d46e00da00e34579ffb6294957da12bf7 100644
--- a/plugins/module_utils/ansible_freeipa_module.py
+++ b/plugins/module_utils/ansible_freeipa_module.py
@@ -29,7 +29,7 @@ __all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env",
            "DEFAULT_CONFIG", "LDAP_GENERALIZED_TIME_FORMAT",
            "kinit_password", "kinit_keytab", "run", "DN", "VERSION",
            "paths", "get_credentials_if_valid", "Encoding",
-           "load_pem_x509_certificate", "DNSName"]
+           "load_pem_x509_certificate", "DNSName", "getargspec"]
 
 import sys
 
@@ -48,7 +48,28 @@ else:
     import gssapi
     from datetime import datetime
     from contextlib import contextmanager
-    import inspect
+
+    # Import getargspec from inspect or provide own getargspec for
+    # Python 2 compatibility with Python 3.11+.
+    try:
+        from inspect import getargspec
+    except ImportError:
+        from collections import namedtuple
+        from inspect import getfullargspec
+
+        # The code is copied from Python 3.10 inspect.py
+        # Authors: Ka-Ping Yee <ping@lfw.org>
+        #          Yury Selivanov <yselivanov@sprymix.com>
+        ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults')
+
+        def getargspec(func):
+            args, varargs, varkw, defaults, kwonlyargs, _kwonlydefaults, \
+                ann = getfullargspec(func)
+            if kwonlyargs or ann:
+                raise ValueError(
+                    "Function has keyword-only parameters or annotations"
+                    ", use inspect.signature() API which can support them")
+            return ArgSpec(args, varargs, varkw, defaults)
 
     # ansible-freeipa requires locale to be C, IPA requires utf-8.
     os.environ["LANGUAGE"] = "C"
@@ -1228,7 +1249,7 @@ else:
             elif result_handler is not None:
                 if "errors" not in handlers_user_args:
                     # pylint: disable=deprecated-method
-                    argspec = inspect.getargspec(result_handler)
+                    argspec = getargspec(result_handler)
                     if "errors" in argspec.args:
                         handlers_user_args["errors"] = _errors
 
diff --git a/roles/ipaclient/library/ipaclient_api.py b/roles/ipaclient/library/ipaclient_api.py
index 346c93a572ed340f6bb5d89c4b0b41ebeb32ab21..763be405504f3d619bbf169b2d41d828cab3fcb9 100644
--- a/roles/ipaclient/library/ipaclient_api.py
+++ b/roles/ipaclient/library/ipaclient_api.py
@@ -75,7 +75,6 @@ subject_base:
 '''
 
 import os
-import inspect
 
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_ipa_client import (
@@ -83,7 +82,7 @@ from ansible.module_utils.ansible_ipa_client import (
     paths, x509, NUM_VERSION, serialization, certdb, api,
     delete_persistent_client_session_data, write_tmp_file,
     ipa_generate_password, CalledProcessError, errors, disable_ra, DN,
-    CLIENT_INSTALL_ERROR, logger
+    CLIENT_INSTALL_ERROR, logger, getargspec
 )
 
 
@@ -134,7 +133,7 @@ def main():
         # Add CA certs to a temporary NSS database
         try:
             # pylint: disable=deprecated-method
-            argspec = inspect.getargspec(tmp_db.create_db)
+            argspec = getargspec(tmp_db.create_db)
             # pylint: enable=deprecated-method
             if "password_filename" not in argspec.args:
                 tmp_db.create_db()
diff --git a/roles/ipaclient/library/ipaclient_setup_nis.py b/roles/ipaclient/library/ipaclient_setup_nis.py
index 289719d94561562801812b3c41be46aef89ed041..f3a4d7d091ba765ef4a1503598b36ebb93458248 100644
--- a/roles/ipaclient/library/ipaclient_setup_nis.py
+++ b/roles/ipaclient/library/ipaclient_setup_nis.py
@@ -57,11 +57,10 @@ EXAMPLES = '''
 RETURN = '''
 '''
 
-import inspect
-
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_ipa_client import (
-    setup_logging, options, sysrestore, paths, configure_nisdomain
+    setup_logging, options, sysrestore, paths, configure_nisdomain,
+    getargspec
 )
 
 
@@ -83,7 +82,7 @@ def main():
     statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
 
     # pylint: disable=deprecated-method
-    argspec = inspect.getargspec(configure_nisdomain)
+    argspec = getargspec(configure_nisdomain)
     # pylint: enable=deprecated-method
     if "statestore" not in argspec.args:
         # NUM_VERSION < 40500:
diff --git a/roles/ipaclient/library/ipaclient_setup_nss.py b/roles/ipaclient/library/ipaclient_setup_nss.py
index 8d1dccae556c4e942e927e1eb59d9d09785d4273..d61a65a9b1eb5809ad351491e133bddc883d9496 100644
--- a/roles/ipaclient/library/ipaclient_setup_nss.py
+++ b/roles/ipaclient/library/ipaclient_setup_nss.py
@@ -141,7 +141,6 @@ RETURN = '''
 
 import os
 import time
-import inspect
 
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_ipa_client import (
@@ -151,7 +150,7 @@ from ansible.module_utils.ansible_ipa_client import (
     get_certs_from_ldap, DN, certstore, x509, logger, certdb,
     CalledProcessError, tasks, client_dns, configure_certmonger, services,
     update_ssh_keys, save_state, configure_ldap_conf, configure_nslcd_conf,
-    configure_openldap_conf, hardcode_ldap_server
+    configure_openldap_conf, hardcode_ldap_server, getargspec
 )
 
 
@@ -323,7 +322,7 @@ def main():
             pass
 
         # pylint: disable=deprecated-method
-        argspec_save_state = inspect.getargspec(save_state)
+        argspec_save_state = getargspec(save_state)
 
         # Name Server Caching Daemon. Disable for SSSD, use otherwise
         # (if installed)
@@ -387,7 +386,7 @@ def main():
         if not options.no_ac:
             # Modify nsswitch/pam stack
             # pylint: disable=deprecated-method
-            argspec = inspect.getargspec(tasks.modify_nsswitch_pam_stack)
+            argspec = getargspec(tasks.modify_nsswitch_pam_stack)
             if "sudo" in argspec.args:
                 tasks.modify_nsswitch_pam_stack(
                     sssd=options.sssd,
diff --git a/roles/ipaclient/library/ipaclient_setup_ntp.py b/roles/ipaclient/library/ipaclient_setup_ntp.py
index 237bb0738c2a195ddc979156991b417e8790d169..7f4a0e8285f4278261614eb9d91e3e636b8a7ad7 100644
--- a/roles/ipaclient/library/ipaclient_setup_ntp.py
+++ b/roles/ipaclient/library/ipaclient_setup_ntp.py
@@ -66,13 +66,11 @@ EXAMPLES = '''
 RETURN = '''
 '''
 
-import inspect
-
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_ipa_client import (
     setup_logging,
     options, sysrestore, paths, sync_time, logger, ipadiscovery,
-    timeconf
+    timeconf, getargspec
 )
 
 
@@ -114,7 +112,7 @@ def main():
         if options.conf_ntp:
             # Attempt to configure and sync time with NTP server (chrony).
             # pylint: disable=deprecated-method
-            argspec = inspect.getargspec(sync_time)
+            argspec = getargspec(sync_time)
             # pylint: enable=deprecated-method
             if "options" not in argspec.args:
                 synced_ntp = sync_time(options.ntp_servers, options.ntp_pool,
diff --git a/roles/ipaclient/library/ipaclient_test.py b/roles/ipaclient/library/ipaclient_test.py
index 540daba423de4721755a82d629187df7d39c0b65..e82d4ed0d634fc100bb89eebf93145c1c573a890 100644
--- a/roles/ipaclient/library/ipaclient_test.py
+++ b/roles/ipaclient/library/ipaclient_test.py
@@ -197,7 +197,6 @@ nosssd_files:
 
 import os
 import socket
-import inspect
 
 try:
     from ansible.module_utils.six.moves.configparser import RawConfigParser
@@ -212,7 +211,7 @@ from ansible.module_utils.ansible_ipa_client import (
     CLIENT_INSTALL_ERROR, tasks, check_ldap_conf, timeconf, constants,
     validate_hostname, nssldap_exists, gssapi, remove_file,
     check_ip_addresses, ipadiscovery, print_port_conf_info,
-    IPA_PYTHON_VERSION
+    IPA_PYTHON_VERSION, getargspec
 )
 
 
@@ -344,7 +343,7 @@ def main():
 
         if options.realm_name:
             # pylint: disable=deprecated-method
-            argspec = inspect.getargspec(validate_domain_name)
+            argspec = getargspec(validate_domain_name)
             if "entity" in argspec.args:
                 # NUM_VERSION >= 40690:
                 validate_domain_name(options.realm_name, entity="realm")
diff --git a/roles/ipaclient/module_utils/ansible_ipa_client.py b/roles/ipaclient/module_utils/ansible_ipa_client.py
index 0523ebb2339bb43d14d703596587db16d4e7fb16..304db30b3e9511c4b414c8dfc7760c2d9ab1972b 100644
--- a/roles/ipaclient/module_utils/ansible_ipa_client.py
+++ b/roles/ipaclient/module_utils/ansible_ipa_client.py
@@ -46,7 +46,7 @@ __all__ = ["gssapi", "version", "ipadiscovery", "api", "errors", "x509",
            "configure_nslcd_conf", "configure_ssh_config",
            "configure_sshd_config", "configure_automount",
            "configure_firefox", "sync_time", "check_ldap_conf",
-           "sssd_enable_ifp"]
+           "sssd_enable_ifp", "getargspec"]
 
 import sys
 
@@ -110,10 +110,31 @@ else:
         # IPA version >= 4.4
 
         # import sys
-        import inspect
         import gssapi
         import logging
 
+        # Import getargspec from inspect or provide own getargspec for
+        # Python 2 compatibility with Python 3.11+.
+        try:
+            from inspect import getargspec
+        except ImportError:
+            from collections import namedtuple
+            from inspect import getfullargspec
+
+            # The code is copied from Python 3.10 inspect.py
+            # Authors: Ka-Ping Yee <ping@lfw.org>
+            #          Yury Selivanov <yselivanov@sprymix.com>
+            ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults')
+
+            def getargspec(func):
+                args, varargs, varkw, defaults, kwonlyargs, _kwonlydefaults, \
+                    ann = getfullargspec(func)
+                if kwonlyargs or ann:
+                    raise ValueError(
+                        "Function has keyword-only parameters or annotations"
+                        ", use inspect.signature() API which can support them")
+                return ArgSpec(args, varargs, varkw, defaults)
+
         from ipapython import version
         try:
             from ipaclient.install import ipadiscovery
@@ -200,7 +221,7 @@ else:
             sys.path.remove(temp_dir)
 
             # pylint: disable=deprecated-method
-            argspec = inspect.getargspec(
+            argspec = getargspec(
                 ipa_client_install.configure_krb5_conf)
             if argspec.keywords is None:
                 def configure_krb5_conf(
@@ -240,7 +261,7 @@ else:
             create_ipa_nssdb = certdb.create_ipa_nssdb
 
             argspec = \
-                inspect.getargspec(ipa_client_install.configure_nisdomain)
+                getargspec(ipa_client_install.configure_nisdomain)
             if len(argspec.args) == 3:
                 configure_nisdomain = ipa_client_install.configure_nisdomain
             else:
diff --git a/roles/ipareplica/library/ipareplica_custodia_import_dm_password.py b/roles/ipareplica/library/ipareplica_custodia_import_dm_password.py
index d6aa61d9423eefeb75b0a01d7c0ff98e98a05588..2a2fe044356b642b357db4ee6cb29dbe193c7f3a 100644
--- a/roles/ipareplica/library/ipareplica_custodia_import_dm_password.py
+++ b/roles/ipareplica/library/ipareplica_custodia_import_dm_password.py
@@ -96,13 +96,13 @@ RETURN = '''
 '''
 
 import os
-import inspect
 
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_ipa_replica import (
     AnsibleModuleLog, setup_logging, installer, DN, paths,
     gen_env_boostrap_finalize_core, constants, api_bootstrap_finalize,
-    gen_ReplicaConfig, gen_remote_api, redirect_stdout, custodiainstance
+    gen_ReplicaConfig, gen_remote_api, redirect_stdout, custodiainstance,
+    getargspec
 )
 
 
@@ -200,7 +200,7 @@ def main():
         ansible_log.debug("-- CUSTODIA IMPORT DM PASSWORD --")
 
         # pylint: disable=deprecated-method
-        argspec = inspect.getargspec(custodia.import_dm_password)
+        argspec = getargspec(custodia.import_dm_password)
         # pylint: enable=deprecated-method
         if "master_host_name" in argspec.args:
             custodia.import_dm_password(config.master_host_name)
diff --git a/roles/ipareplica/library/ipareplica_setup_ds.py b/roles/ipareplica/library/ipareplica_setup_ds.py
index 91d30751da9dbe36342c9ad1fa78f49fa2ab66e9..c3c4c8645f50245c4e74ca9e7dc5c86f3f1013ae 100644
--- a/roles/ipareplica/library/ipareplica_setup_ds.py
+++ b/roles/ipareplica/library/ipareplica_setup_ds.py
@@ -149,7 +149,6 @@ RETURN = '''
 '''
 
 import os
-import inspect
 
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_ipa_replica import (
@@ -157,7 +156,8 @@ from ansible.module_utils.ansible_ipa_replica import (
     ansible_module_get_parsed_ip_addresses,
     gen_env_boostrap_finalize_core, constants, api_bootstrap_finalize,
     gen_ReplicaConfig, gen_remote_api, redirect_stdout, ipaldap,
-    install_replica_ds, install_dns_records, ntpinstance, ScriptError
+    install_replica_ds, install_dns_records, ntpinstance, ScriptError,
+    getargspec
 )
 
 
@@ -317,7 +317,7 @@ def main():
         # Configure dirsrv
         with redirect_stdout(ansible_log):
             # pylint: disable=deprecated-method
-            argspec = inspect.getargspec(install_replica_ds)
+            argspec = getargspec(install_replica_ds)
             # pylint: enable=deprecated-method
             if "promote" in argspec.args:
                 ds = install_replica_ds(config, options, ca_enabled,
@@ -343,7 +343,7 @@ def main():
         # pylint: enable=deprecated-method
         # Always try to install DNS records
         # pylint: disable=deprecated-method
-        argspec = inspect.getargspec(install_dns_records)
+        argspec = getargspec(install_dns_records)
         # pylint: enable=deprecated-method
         if "fstore" not in argspec.args:
             install_dns_records(config, options, remote_api)
diff --git a/roles/ipareplica/library/ipareplica_setup_http.py b/roles/ipareplica/library/ipareplica_setup_http.py
index 015dafd228ef83724d78fa6ce720762af90d2fb1..2d26d18cf776a5710a62a2257c62d31bc297437f 100644
--- a/roles/ipareplica/library/ipareplica_setup_http.py
+++ b/roles/ipareplica/library/ipareplica_setup_http.py
@@ -90,14 +90,13 @@ RETURN = '''
 '''
 
 import os
-import inspect
 
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_ipa_replica import (
     AnsibleModuleLog, setup_logging, installer, DN, paths, sysrestore,
     gen_env_boostrap_finalize_core, constants, api_bootstrap_finalize,
     gen_ReplicaConfig, gen_remote_api, api, redirect_stdout, create_ipa_conf,
-    install_http
+    install_http, getargspec
 )
 
 
@@ -203,7 +202,7 @@ def main():
                         master=config.master_host_name)
 
         # pylint: disable=deprecated-method
-        argspec = inspect.getargspec(install_http)
+        argspec = getargspec(install_http)
         # pylint: enable=deprecated-method
         if "promote" in argspec.args:
             install_http(
diff --git a/roles/ipareplica/library/ipareplica_setup_krb.py b/roles/ipareplica/library/ipareplica_setup_krb.py
index cb835959324c27dca14067e12a02f3fbb3b3014f..423f4dec64e21fc71e65c8df315d138118679421 100644
--- a/roles/ipareplica/library/ipareplica_setup_krb.py
+++ b/roles/ipareplica/library/ipareplica_setup_krb.py
@@ -78,13 +78,12 @@ RETURN = '''
 '''
 
 import os
-import inspect
 
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_ipa_replica import (
     AnsibleModuleLog, setup_logging, installer, DN, paths, sysrestore,
     gen_env_boostrap_finalize_core, constants, api_bootstrap_finalize,
-    gen_ReplicaConfig, api, redirect_stdout, install_krb
+    gen_ReplicaConfig, api, redirect_stdout, install_krb, getargspec
 )
 
 
@@ -162,7 +161,7 @@ def main():
 
     with redirect_stdout(ansible_log):
         # pylint: disable=deprecated-method
-        argspec = inspect.getargspec(install_krb)
+        argspec = getargspec(install_krb)
         # pylint: enable=deprecated-method
         if "promote" in argspec.args:
             install_krb(
diff --git a/roles/ipareplica/library/ipareplica_test.py b/roles/ipareplica/library/ipareplica_test.py
index 29b3af7f91daefe964d92815cb1d3af6c8751356..7ec6fb1617703c88c81fe052c24f571024ee7c8a 100644
--- a/roles/ipareplica/library/ipareplica_test.py
+++ b/roles/ipareplica/library/ipareplica_test.py
@@ -136,7 +136,6 @@ RETURN = '''
 '''
 
 import os
-import inspect
 
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_ipa_replica import (
@@ -144,7 +143,7 @@ from ansible.module_utils.ansible_ipa_replica import (
     ansible_module_get_parsed_ip_addresses, service,
     redirect_stdout, create_ipa_conf, ipautil,
     x509, validate_domain_name, common_check,
-    IPA_PYTHON_VERSION
+    IPA_PYTHON_VERSION, getargspec
 )
 
 
@@ -287,7 +286,7 @@ def main():
     # create_ipa_conf has the additional master argument.
     change_master_for_certmonger = False
     # pylint: disable=deprecated-method
-    argspec = inspect.getargspec(create_ipa_conf)
+    argspec = getargspec(create_ipa_conf)
     # pylint: enable=deprecated-method
     if "master" in argspec.args:
         change_master_for_certmonger = True
@@ -421,7 +420,7 @@ def main():
     try:
         with redirect_stdout(ansible_log):
             # pylint: disable=deprecated-method
-            argspec = inspect.getargspec(common_check)
+            argspec = getargspec(common_check)
             # pylint: enable=deprecated-method
             if "skip_mem_check" in argspec.args:
                 common_check(options.no_ntp, options.skip_mem_check,
diff --git a/roles/ipareplica/module_utils/ansible_ipa_replica.py b/roles/ipareplica/module_utils/ansible_ipa_replica.py
index 10624b1f2b6a1d2c82b3ba0714d5286012047d1d..5b6872316c1d4240afc58dae683fa5acf711677e 100644
--- a/roles/ipareplica/module_utils/ansible_ipa_replica.py
+++ b/roles/ipareplica/module_utils/ansible_ipa_replica.py
@@ -46,7 +46,7 @@ __all__ = ["contextlib", "dnsexception", "dnsresolver", "dnsreversename",
            "common_check", "current_domain_level",
            "check_domain_level_is_supported", "promotion_check_ipa_domain",
            "SSSDConfig", "CalledProcessError", "timeconf", "ntpinstance",
-           "dnsname", "kernel_keyring", "krbinstance"]
+           "dnsname", "kernel_keyring", "krbinstance", "getargspec"]
 
 import sys
 
@@ -59,6 +59,28 @@ else:
     import logging
     from contextlib import contextmanager as contextlib_contextmanager
 
+    # Import getargspec from inspect or provide own getargspec for
+    # Python 2 compatibility with Python 3.11+.
+    try:
+        from inspect import getargspec
+    except ImportError:
+        from collections import namedtuple
+        from inspect import getfullargspec
+
+        # The code is copied from Python 3.10 inspect.py
+        # Authors: Ka-Ping Yee <ping@lfw.org>
+        #          Yury Selivanov <yselivanov@sprymix.com>
+        ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults')
+
+        def getargspec(func):
+            args, varargs, varkw, defaults, kwonlyargs, _kwonlydefaults, \
+                ann = getfullargspec(func)
+            if kwonlyargs or ann:
+                raise ValueError(
+                    "Function has keyword-only parameters or annotations"
+                    ", use inspect.signature() API which can support them")
+            return ArgSpec(args, varargs, varkw, defaults)
+
     from ipapython.version import NUM_VERSION, VERSION
 
     if NUM_VERSION < 30201:
diff --git a/roles/ipaserver/library/ipaserver_setup_ntp.py b/roles/ipaserver/library/ipaserver_setup_ntp.py
index 723890538f76460dfcc6745865e348467e4f58bd..c2292ebe1dc458de6e2e5d5039bbca00cb195963 100644
--- a/roles/ipaserver/library/ipaserver_setup_ntp.py
+++ b/roles/ipaserver/library/ipaserver_setup_ntp.py
@@ -53,12 +53,11 @@ EXAMPLES = '''
 RETURN = '''
 '''
 
-import inspect
-
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_ipa_server import (
     AnsibleModuleLog, setup_logging, options, sysrestore, paths,
-    redirect_stdout, time_service, sync_time, ntpinstance, timeconf
+    redirect_stdout, time_service, sync_time, ntpinstance, timeconf,
+    getargspec
 )
 
 
@@ -94,7 +93,7 @@ def main():
         ansible_module.log("Synchronizing time")
 
         # pylint: disable=deprecated-method
-        argspec = inspect.getargspec(sync_time)
+        argspec = getargspec(sync_time)
         # pylint: enable=deprecated-method
         if "options" not in argspec.args:
             synced_ntp = sync_time(options.ntp_servers, options.ntp_pool,
diff --git a/roles/ipaserver/library/ipaserver_test.py b/roles/ipaserver/library/ipaserver_test.py
index 76173b02bb15e3eddb774ce3655d32ebe987ccb7..d619add0ef5ae1e2e7620cc6ccbd9071c585f959 100644
--- a/roles/ipaserver/library/ipaserver_test.py
+++ b/roles/ipaserver/library/ipaserver_test.py
@@ -212,7 +212,6 @@ RETURN = '''
 
 import os
 import sys
-import inspect
 import random
 from shutil import copyfile
 
@@ -226,7 +225,7 @@ from ansible.module_utils.ansible_ipa_server import (
     read_cache, ca, tasks, check_ldap_conf, timeconf, httpinstance,
     check_dirsrv, ScriptError, get_fqdn, verify_fqdn, BadHostError,
     validate_domain_name, load_pkcs12, IPA_PYTHON_VERSION,
-    encode_certificate, check_available_memory
+    encode_certificate, check_available_memory, getargspec
 )
 from ansible.module_utils import six
 
@@ -944,7 +943,7 @@ def main():
         realm_name = options.realm_name.upper()
 
     # pylint: disable=deprecated-method
-    argspec = inspect.getargspec(validate_domain_name)
+    argspec = getargspec(validate_domain_name)
     # pylint: enable=deprecated-method
     if "entity" in argspec.args:
         # NUM_VERSION >= 40690:
diff --git a/roles/ipaserver/module_utils/ansible_ipa_server.py b/roles/ipaserver/module_utils/ansible_ipa_server.py
index 36ee5d6b9ea35b0680750777ccecece2d14f11d9..aba6b68a393de19ca5180e40d119cdaf49cfdec6 100644
--- a/roles/ipaserver/module_utils/ansible_ipa_server.py
+++ b/roles/ipaserver/module_utils/ansible_ipa_server.py
@@ -41,7 +41,7 @@ __all__ = ["IPAChangeConf", "certmonger", "sysrestore", "root_logger",
            "adtrustinstance", "IPAAPI_USER", "sync_time", "PKIIniLoader",
            "default_subject_base", "default_ca_subject_dn",
            "check_ldap_conf", "encode_certificate", "decode_certificate",
-           "check_available_memory"]
+           "check_available_memory", "getargspec"]
 
 import sys
 
@@ -58,6 +58,28 @@ else:
     from ansible.module_utils import six
     import base64
 
+    # Import getargspec from inspect or provide own getargspec for
+    # Python 2 compatibility with Python 3.11+.
+    try:
+        from inspect import getargspec
+    except ImportError:
+        from collections import namedtuple
+        from inspect import getfullargspec
+
+        # The code is copied from Python 3.10 inspect.py
+        # Authors: Ka-Ping Yee <ping@lfw.org>
+        #          Yury Selivanov <yselivanov@sprymix.com>
+        ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults')
+
+        def getargspec(func):
+            args, varargs, varkw, defaults, kwonlyargs, _kwonlydefaults, \
+                ann = getfullargspec(func)
+            if kwonlyargs or ann:
+                raise ValueError(
+                    "Function has keyword-only parameters or annotations"
+                    ", use inspect.signature() API which can support them")
+            return ArgSpec(args, varargs, varkw, defaults)
+
     from ipapython.version import NUM_VERSION, VERSION
 
     if NUM_VERSION < 30201: