mirror of
				https://github.com/ansible-collections/community.general.git
				synced 2025-10-26 13:56:09 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			214 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/python
 | |
| # -*- coding: utf-8 -*-
 | |
| # Copyright (c) 2018 Ansible Project
 | |
| # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
 | |
| # SPDX-License-Identifier: GPL-3.0-or-later
 | |
| 
 | |
| from __future__ import (absolute_import, division, print_function)
 | |
| __metaclass__ = type
 | |
| 
 | |
| DOCUMENTATION = '''
 | |
| module: python_requirements_info
 | |
| short_description: Show python path and assert dependency versions
 | |
| description:
 | |
|   - Get info about available Python requirements on the target host, including listing required libraries and gathering versions.
 | |
| extends_documentation_fragment:
 | |
|   - community.general.attributes
 | |
|   - community.general.attributes.info_module
 | |
| options:
 | |
|   dependencies:
 | |
|     type: list
 | |
|     elements: str
 | |
|     description: >
 | |
|       A list of version-likes or module names to check for installation.
 | |
|       Supported operators: <, >, <=, >=, or ==. The bare module name like
 | |
|       V(ansible), the module with a specific version like V(boto3==1.6.1), or a
 | |
|       partial version like V(requests>2) are all valid specifications.
 | |
|     default: []
 | |
| author:
 | |
|   - Will Thames (@willthames)
 | |
|   - Ryan Scott Brown (@ryansb)
 | |
| '''
 | |
| 
 | |
| EXAMPLES = '''
 | |
| - name: Show python lib/site paths
 | |
|   community.general.python_requirements_info:
 | |
| 
 | |
| - name: Check for modern boto3 and botocore versions
 | |
|   community.general.python_requirements_info:
 | |
|     dependencies:
 | |
|       - boto3>1.6
 | |
|       - botocore<2
 | |
| '''
 | |
| 
 | |
| RETURN = '''
 | |
| python:
 | |
|   description: path to python version used
 | |
|   returned: always
 | |
|   type: str
 | |
|   sample: /usr/local/opt/python@2/bin/python2.7
 | |
| python_version:
 | |
|   description: version of python
 | |
|   returned: always
 | |
|   type: str
 | |
|   sample: "2.7.15 (default, May  1 2018, 16:44:08)\n[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)]"
 | |
| python_version_info:
 | |
|   description: breakdown version of python
 | |
|   returned: always
 | |
|   type: dict
 | |
|   contains:
 | |
|     major:
 | |
|       description: The C(major) component of the python interpreter version.
 | |
|       returned: always
 | |
|       type: int
 | |
|       sample: 3
 | |
|     minor:
 | |
|       description: The C(minor) component of the python interpreter version.
 | |
|       returned: always
 | |
|       type: int
 | |
|       sample: 8
 | |
|     micro:
 | |
|       description: The C(micro) component of the python interpreter version.
 | |
|       returned: always
 | |
|       type: int
 | |
|       sample: 10
 | |
|     releaselevel:
 | |
|       description: The C(releaselevel) component of the python interpreter version.
 | |
|       returned: always
 | |
|       type: str
 | |
|       sample: final
 | |
|     serial:
 | |
|       description: The C(serial) component of the python interpreter version.
 | |
|       returned: always
 | |
|       type: int
 | |
|       sample: 0
 | |
|   version_added: 4.2.0
 | |
| python_system_path:
 | |
|   description: List of paths python is looking for modules in
 | |
|   returned: always
 | |
|   type: list
 | |
|   sample:
 | |
|     - /usr/local/opt/python@2/site-packages/
 | |
|     - /usr/lib/python/site-packages/
 | |
| valid:
 | |
|   description: A dictionary of dependencies that matched their desired versions. If no version was specified, then RV(ignore:desired) will be null
 | |
|   returned: always
 | |
|   type: dict
 | |
|   sample:
 | |
|     boto3:
 | |
|       desired: null
 | |
|       installed: 1.7.60
 | |
|     botocore:
 | |
|       desired: botocore<2
 | |
|       installed: 1.10.60
 | |
| mismatched:
 | |
|   description: A dictionary of dependencies that did not satisfy the desired version
 | |
|   returned: always
 | |
|   type: dict
 | |
|   sample:
 | |
|     botocore:
 | |
|       desired: botocore>2
 | |
|       installed: 1.10.60
 | |
| not_found:
 | |
|   description: A list of packages that could not be imported at all, and are not installed
 | |
|   returned: always
 | |
|   type: list
 | |
|   sample:
 | |
|     - boto4
 | |
|     - requests
 | |
| '''
 | |
| 
 | |
| import re
 | |
| import sys
 | |
| import operator
 | |
| 
 | |
| HAS_DISTUTILS = False
 | |
| try:
 | |
|     import pkg_resources
 | |
|     from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
 | |
|     HAS_DISTUTILS = True
 | |
| except ImportError:
 | |
|     pass
 | |
| 
 | |
| from ansible.module_utils.basic import AnsibleModule
 | |
| 
 | |
| operations = {
 | |
|     '<=': operator.le,
 | |
|     '>=': operator.ge,
 | |
|     '<': operator.lt,
 | |
|     '>': operator.gt,
 | |
|     '==': operator.eq,
 | |
| }
 | |
| 
 | |
| python_version_info = dict(
 | |
|     major=sys.version_info[0],
 | |
|     minor=sys.version_info[1],
 | |
|     micro=sys.version_info[2],
 | |
|     releaselevel=sys.version_info[3],
 | |
|     serial=sys.version_info[4],
 | |
| )
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     module = AnsibleModule(
 | |
|         argument_spec=dict(
 | |
|             dependencies=dict(type='list', elements='str', default=[])
 | |
|         ),
 | |
|         supports_check_mode=True,
 | |
|     )
 | |
|     if not HAS_DISTUTILS:
 | |
|         module.fail_json(
 | |
|             msg='Could not import "distutils" and "pkg_resources" libraries to introspect python environment.',
 | |
|             python=sys.executable,
 | |
|             python_version=sys.version,
 | |
|             python_version_info=python_version_info,
 | |
|             python_system_path=sys.path,
 | |
|         )
 | |
|     pkg_dep_re = re.compile(r'(^[a-zA-Z][a-zA-Z0-9_-]+)(?:(==|[><]=?)([0-9.]+))?$')
 | |
| 
 | |
|     results = dict(
 | |
|         not_found=[],
 | |
|         mismatched={},
 | |
|         valid={},
 | |
|     )
 | |
| 
 | |
|     for dep in module.params['dependencies']:
 | |
|         match = pkg_dep_re.match(dep)
 | |
|         if not match:
 | |
|             module.fail_json(msg="Failed to parse version requirement '{0}'. Must be formatted like 'ansible>2.6'".format(dep))
 | |
|         pkg, op, version = match.groups()
 | |
|         if op is not None and op not in operations:
 | |
|             module.fail_json(msg="Failed to parse version requirement '{0}'. Operator must be one of >, <, <=, >=, or ==".format(dep))
 | |
|         try:
 | |
|             existing = pkg_resources.get_distribution(pkg).version
 | |
|         except pkg_resources.DistributionNotFound:
 | |
|             # not there
 | |
|             results['not_found'].append(pkg)
 | |
|             continue
 | |
|         if op is None and version is None:
 | |
|             results['valid'][pkg] = {
 | |
|                 'installed': existing,
 | |
|                 'desired': None,
 | |
|             }
 | |
|         elif operations[op](LooseVersion(existing), LooseVersion(version)):
 | |
|             results['valid'][pkg] = {
 | |
|                 'installed': existing,
 | |
|                 'desired': dep,
 | |
|             }
 | |
|         else:
 | |
|             results['mismatched'][pkg] = {
 | |
|                 'installed': existing,
 | |
|                 'desired': dep,
 | |
|             }
 | |
| 
 | |
|     module.exit_json(
 | |
|         python=sys.executable,
 | |
|         python_version=sys.version,
 | |
|         python_version_info=python_version_info,
 | |
|         python_system_path=sys.path,
 | |
|         **results
 | |
|     )
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     main()
 |