Add module common code to allow it to be easier to indicate whether arguments are mutually exclusive, required in conjunction, or whether one of a list of arguments is required. This simplifies writing Python modules.
This commit is contained in:
parent
98c350a6ac
commit
1e4d45af1e
4 changed files with 61 additions and 52 deletions
|
@ -55,11 +55,14 @@ except ImportError:
|
||||||
|
|
||||||
class AnsibleModule(object):
|
class AnsibleModule(object):
|
||||||
|
|
||||||
def __init__(self, argument_spec, bypass_checks=False, no_log=False, check_invalid_arguments=True):
|
def __init__(self, argument_spec, bypass_checks=False, no_log=False,
|
||||||
|
check_invalid_arguments=True, mutually_exclusive=None, required_together=None,
|
||||||
|
required_one_of=None):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
common code for quickly building an ansible module in Python
|
common code for quickly building an ansible module in Python
|
||||||
(although you can write modules in anything that can return JSON)
|
(although you can write modules in anything that can return JSON)
|
||||||
see library/slurp and others for examples
|
see library/* for examples
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.argument_spec = argument_spec
|
self.argument_spec = argument_spec
|
||||||
|
@ -77,12 +80,14 @@ class AnsibleModule(object):
|
||||||
if not bypass_checks:
|
if not bypass_checks:
|
||||||
self._check_required_arguments()
|
self._check_required_arguments()
|
||||||
self._check_argument_types()
|
self._check_argument_types()
|
||||||
|
self._check_mutually_exclusive(mutually_exclusive)
|
||||||
|
self._check_required_together(required_together)
|
||||||
|
self._check_required_one_of(required_one_of)
|
||||||
|
|
||||||
self._set_defaults(pre=False)
|
self._set_defaults(pre=False)
|
||||||
if not no_log:
|
if not no_log:
|
||||||
self._log_invocation()
|
self._log_invocation()
|
||||||
|
|
||||||
|
|
||||||
def _handle_aliases(self):
|
def _handle_aliases(self):
|
||||||
for (k,v) in self.argument_spec.iteritems():
|
for (k,v) in self.argument_spec.iteritems():
|
||||||
self._legal_inputs.append(k)
|
self._legal_inputs.append(k)
|
||||||
|
@ -106,6 +111,39 @@ class AnsibleModule(object):
|
||||||
if k not in self._legal_inputs:
|
if k not in self._legal_inputs:
|
||||||
self.fail_json(msg="unsupported parameter for module: %s" % k)
|
self.fail_json(msg="unsupported parameter for module: %s" % k)
|
||||||
|
|
||||||
|
def _count_terms(self, check):
|
||||||
|
count = 0
|
||||||
|
for term in check:
|
||||||
|
if term in self.params:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
def _check_mutually_exclusive(self, spec):
|
||||||
|
if spec is None:
|
||||||
|
return
|
||||||
|
for check in spec:
|
||||||
|
count = self._count_terms(check)
|
||||||
|
if count > 1:
|
||||||
|
self.fail_json(msg="parameters are mutually exclusive: %s" % check)
|
||||||
|
|
||||||
|
def _check_required_one_of(self, spec):
|
||||||
|
if spec is None:
|
||||||
|
return
|
||||||
|
for check in spec:
|
||||||
|
count = self._count_terms(check)
|
||||||
|
if count == 0:
|
||||||
|
self.fail_json(msg="one of the following is required: %s" % check)
|
||||||
|
|
||||||
|
def _check_required_together(self, spec):
|
||||||
|
if spec is None:
|
||||||
|
return
|
||||||
|
for check in spec:
|
||||||
|
counts = [ self.count_terms([field]) for field in check ]
|
||||||
|
non_zero = [ c for c in counts if c > 0 ]
|
||||||
|
if len(non_zero) > 0:
|
||||||
|
if 0 in counts:
|
||||||
|
self.fail_json(msg="parameters are required together: %s" % check)
|
||||||
|
|
||||||
def _check_required_arguments(self):
|
def _check_required_arguments(self):
|
||||||
''' ensure all required arguments are present '''
|
''' ensure all required arguments are present '''
|
||||||
missing = []
|
missing = []
|
||||||
|
|
42
library/pip
42
library/pip
|
@ -90,7 +90,11 @@ def main():
|
||||||
virtualenv=dict(default=None, required=False)
|
virtualenv=dict(default=None, required=False)
|
||||||
)
|
)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=arg_spec)
|
module = AnsibleModule(
|
||||||
|
argument_spec=arg_spec,
|
||||||
|
required_one_of=[['name','requirements']],
|
||||||
|
mutually_exclusive=[['name','requirements']],
|
||||||
|
)
|
||||||
|
|
||||||
rc = 0
|
rc = 0
|
||||||
err = ''
|
err = ''
|
||||||
|
@ -115,38 +119,19 @@ def main():
|
||||||
command_map = dict(present='install', absent='uninstall', latest='install')
|
command_map = dict(present='install', absent='uninstall', latest='install')
|
||||||
|
|
||||||
if state == 'latest' and version is not None:
|
if state == 'latest' and version is not None:
|
||||||
module.fail_json(msg='If `state` is set to `latest` the `version` '
|
module.fail_json(msg='version is incompatible with state=latest')
|
||||||
'parameter must not be specified.')
|
|
||||||
|
|
||||||
if state == 'latest' and requirements is not None:
|
if state == 'latest' and requirements is not None:
|
||||||
module.fail_json(msg='If `state` is set to `latest` the `requirements` '
|
module.fail_json(msg='requirements is incompatible with state=latest')
|
||||||
'parameter must not be specified.')
|
|
||||||
|
|
||||||
if name is not None and '==' in name:
|
if name is not None and '=' in name:
|
||||||
module.fail_json(msg='It looks like you specified the version number '
|
module.fail_json(msg='versions must be specified in the version= parameter')
|
||||||
'in the library name. Use the `version` parameter '
|
|
||||||
'to specify version instead')
|
|
||||||
|
|
||||||
if version is not None and name is None:
|
|
||||||
module.fail_json(msg='The `version` parameter must be used with the '
|
|
||||||
'`name` parameter and not with the `requirements` '
|
|
||||||
'paramter')
|
|
||||||
|
|
||||||
if name is None and requirements is None:
|
|
||||||
module.fail_json(msg='You must specify a python library name via '
|
|
||||||
'the `name` parameter or a requirements file via '
|
|
||||||
'the `requirements` paramter')
|
|
||||||
|
|
||||||
if name and requirements:
|
|
||||||
module.fail_json(msg='Both `name` and `requirements` were specified. '
|
|
||||||
'Specify only the python library name via the '
|
|
||||||
'`name` parameter or a requirements file via the '
|
|
||||||
'`requirements` parameter')
|
|
||||||
|
|
||||||
cmd = None
|
cmd = None
|
||||||
installed = None
|
installed = None
|
||||||
|
|
||||||
if requirements:
|
if requirements:
|
||||||
|
|
||||||
cmd = '%s %s -r %s --use-mirrors' % (pip, command_map[state], requirements)
|
cmd = '%s %s -r %s --use-mirrors' % (pip, command_map[state], requirements)
|
||||||
rc_pip, out_pip, err_pip = _run(cmd)
|
rc_pip, out_pip, err_pip = _run(cmd)
|
||||||
|
|
||||||
|
@ -158,8 +143,8 @@ def main():
|
||||||
(not _did_install(out) and state == 'absent'))
|
(not _did_install(out) and state == 'absent'))
|
||||||
|
|
||||||
if name and state == 'latest':
|
if name and state == 'latest':
|
||||||
cmd = '%s %s %s --upgrade' % (pip, command_map[state], name)
|
|
||||||
|
|
||||||
|
cmd = '%s %s %s --upgrade' % (pip, command_map[state], name)
|
||||||
rc_pip, out_pip, err_pip = _run(cmd)
|
rc_pip, out_pip, err_pip = _run(cmd)
|
||||||
|
|
||||||
rc += rc_pip
|
rc += rc_pip
|
||||||
|
@ -169,8 +154,8 @@ def main():
|
||||||
changed = 'Successfully installed' in out_pip
|
changed = 'Successfully installed' in out_pip
|
||||||
|
|
||||||
elif name:
|
elif name:
|
||||||
installed = _is_package_installed(name, pip, version)
|
|
||||||
|
|
||||||
|
installed = _is_package_installed(name, pip, version)
|
||||||
changed = ((installed and state == 'absent') or
|
changed = ((installed and state == 'absent') or
|
||||||
(not installed and state == 'present'))
|
(not installed and state == 'present'))
|
||||||
|
|
||||||
|
@ -188,8 +173,7 @@ def main():
|
||||||
cmd = cmd + ' --use-mirrors'
|
cmd = cmd + ' --use-mirrors'
|
||||||
|
|
||||||
rc_pip, out_pip, err_pip = _run(cmd)
|
rc_pip, out_pip, err_pip = _run(cmd)
|
||||||
|
rc += rc_pip
|
||||||
rc += rc_pip
|
|
||||||
out += out_pip
|
out += out_pip
|
||||||
err += err_pip
|
err += err_pip
|
||||||
|
|
||||||
|
|
0
library/shell
Normal file → Executable file
0
library/shell
Normal file → Executable file
27
library/yum
27
library/yum
|
@ -35,7 +35,6 @@ def is_installed(repoq, pkgspec, qf=def_qf):
|
||||||
rc,out,err = run(cmd)
|
rc,out,err = run(cmd)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
return [ p for p in out.split('\n') if p.strip() ]
|
return [ p for p in out.split('\n') if p.strip() ]
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def is_available(repoq, pkgspec, qf=def_qf):
|
def is_available(repoq, pkgspec, qf=def_qf):
|
||||||
|
@ -43,10 +42,8 @@ def is_available(repoq, pkgspec, qf=def_qf):
|
||||||
rc,out,err = run(cmd)
|
rc,out,err = run(cmd)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
return [ p for p in out.split('\n') if p.strip() ]
|
return [ p for p in out.split('\n') if p.strip() ]
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def is_update(repoq, pkgspec, qf=def_qf):
|
def is_update(repoq, pkgspec, qf=def_qf):
|
||||||
cmd = repoq + ["--pkgnarrow=updates", "--qf", qf, pkgspec]
|
cmd = repoq + ["--pkgnarrow=updates", "--qf", qf, pkgspec]
|
||||||
rc,out,err = run(cmd)
|
rc,out,err = run(cmd)
|
||||||
|
@ -55,17 +52,14 @@ def is_update(repoq, pkgspec, qf=def_qf):
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def what_provides(repoq, req_spec, qf=def_qf):
|
def what_provides(repoq, req_spec, qf=def_qf):
|
||||||
cmd = repoq + ["--qf", qf, "--whatprovides", req_spec]
|
cmd = repoq + ["--qf", qf, "--whatprovides", req_spec]
|
||||||
rc,out,err = run(cmd)
|
rc,out,err = run(cmd)
|
||||||
ret = []
|
ret = []
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
ret = set([ p for p in out.split('\n') if p.strip() ])
|
ret = set([ p for p in out.split('\n') if p.strip() ])
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def pkg_to_dict(pkgstr):
|
def pkg_to_dict(pkgstr):
|
||||||
if pkgstr.strip():
|
if pkgstr.strip():
|
||||||
n,e,v,r,a,repo = pkgstr.split('|')
|
n,e,v,r,a,repo = pkgstr.split('|')
|
||||||
|
@ -80,7 +74,7 @@ def pkg_to_dict(pkgstr):
|
||||||
'version':v,
|
'version':v,
|
||||||
'repo':repo,
|
'repo':repo,
|
||||||
'nevra': '%s:%s-%s-%s.%s' % (e,n,v,r,a)
|
'nevra': '%s:%s-%s-%s.%s' % (e,n,v,r,a)
|
||||||
}
|
}
|
||||||
|
|
||||||
if repo == 'installed':
|
if repo == 'installed':
|
||||||
d['yumstate'] = 'installed'
|
d['yumstate'] = 'installed'
|
||||||
|
@ -95,7 +89,6 @@ def repolist(repoq, qf="%{repoid}"):
|
||||||
ret = []
|
ret = []
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
ret = set([ p for p in out.split('\n') if p.strip() ])
|
ret = set([ p for p in out.split('\n') if p.strip() ])
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def list_stuff(conf_file, stuff):
|
def list_stuff(conf_file, stuff):
|
||||||
|
@ -128,7 +121,6 @@ def run(command):
|
||||||
rc = 1
|
rc = 1
|
||||||
err = traceback.format_exc()
|
err = traceback.format_exc()
|
||||||
out = ''
|
out = ''
|
||||||
|
|
||||||
if out is None:
|
if out is None:
|
||||||
out = ''
|
out = ''
|
||||||
if err is None:
|
if err is None:
|
||||||
|
@ -429,15 +421,13 @@ def main():
|
||||||
state=dict(default='installed', choices=['absent','present','installed','removed','latest']),
|
state=dict(default='installed', choices=['absent','present','installed','removed','latest']),
|
||||||
list=dict(),
|
list=dict(),
|
||||||
conf_file=dict(default=None),
|
conf_file=dict(default=None),
|
||||||
)
|
),
|
||||||
|
required_one_of = [['pkg','list']],
|
||||||
|
mutually_exclusive = [['pkg','list']]
|
||||||
)
|
)
|
||||||
|
|
||||||
params = module.params
|
params = module.params
|
||||||
|
|
||||||
|
|
||||||
if params['list'] and params['pkg']:
|
|
||||||
module.fail_json(msg="expected 'list=' or 'name=', but not both")
|
|
||||||
|
|
||||||
if params['list']:
|
if params['list']:
|
||||||
if not os.path.exists(repoquery):
|
if not os.path.exists(repoquery):
|
||||||
module.fail_json(msg="%s is required to use list= with this module. Please install the yum-utils package." % repoquery)
|
module.fail_json(msg="%s is required to use list= with this module. Please install the yum-utils package." % repoquery)
|
||||||
|
@ -446,12 +436,9 @@ def main():
|
||||||
|
|
||||||
else:
|
else:
|
||||||
pkg = params['pkg']
|
pkg = params['pkg']
|
||||||
if pkg is None:
|
state = params['state']
|
||||||
module.fail_json(msg="expected 'list=' or 'name='")
|
res = ensure(module, state, pkg, params['conf_file'])
|
||||||
else:
|
module.fail_json(msg="we should never get here unless this all failed", **res)
|
||||||
state = params['state']
|
|
||||||
res = ensure(module, state, pkg, params['conf_file'])
|
|
||||||
module.fail_json(msg="we should never get here unless this all failed", **res)
|
|
||||||
|
|
||||||
# this is magic, see lib/ansible/module_common.py
|
# this is magic, see lib/ansible/module_common.py
|
||||||
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
||||||
|
|
Loading…
Reference in a new issue