Merge remote branch 'upstream/devel' into devel
This commit is contained in:
commit
7c2c6fee3a
8 changed files with 141 additions and 32 deletions
|
@ -303,7 +303,19 @@ directory.
|
|||
Lookup Plugins
|
||||
--------------
|
||||
|
||||
Language constructs like "with_fileglob" are implemnted via lookup plugins. Just like other plugin types, you can write your own.
|
||||
Language constructs like "with_fileglob" and "with_items" are implemented via lookup plugins. Just like other plugin types, you can write your own.
|
||||
|
||||
Vars Plugins
|
||||
------------
|
||||
|
||||
Playbook constructs like 'host_vars' and 'group_vars' work via 'vars' plugins. They inject additional variable
|
||||
data into ansible runs that did not come from an inventory, playbook, or command line. Note that variables
|
||||
can also be returned from inventory, so in most cases, you won't need to write or understand vars_plugins.
|
||||
|
||||
Filter Plugins
|
||||
--------------
|
||||
|
||||
If you want more Jinja2 filters available in a Jinja2 template (filters like to_yaml and to_json are provided by default), they can be extended by writing a filter plugin.
|
||||
|
||||
Distributing Plugins
|
||||
--------------------
|
||||
|
@ -317,6 +329,8 @@ to /usr/share/ansible/plugins, in a subfolder for each plugin type::
|
|||
* lookup_plugins
|
||||
* callback_plugins
|
||||
* connection_plugins
|
||||
* filter_plugins
|
||||
* vars_plugins
|
||||
|
||||
To change this path, edit the ansible configuration file.
|
||||
|
||||
|
|
|
@ -226,6 +226,8 @@ also works with ``ansible-playbook``::
|
|||
|
||||
$ ansible webservers:dbservers -m command -a "/bin/foo xyz" --limit region
|
||||
|
||||
Assuming version 0.9 or later, as with other host patterns, values to limit can be seperated with ";", ":", or ",".
|
||||
|
||||
Now let's talk about range selection. Suppose you have 1000 servers in group 'datacenter', but only want to target one at a time. This is also easy::
|
||||
|
||||
$ ansible webservers[0-99] -m command -a "/bin/foo xyz"
|
||||
|
|
|
@ -288,6 +288,47 @@ While `only_if` is a pretty good option for advanced users, it exposes more guts
|
|||
we can do better. In 0.9, we will be adding `when`, which will be like a syntactic sugar for `only_if` and hide
|
||||
this level of complexity -- it will numerous built in operators.
|
||||
|
||||
Conditional Execution (Simplified)
|
||||
``````````````````````````````````
|
||||
|
||||
In Ansible 0.9, we realized that only_if was a bit syntactically complicated, and exposed too much Python
|
||||
to the user. As a result, the 'when' set of keywords was added. The 'when' statements do not have
|
||||
to be quoted or casted to specify types, but you should seperate any variables used with whitespace. In
|
||||
most cases users will be able to use 'when', but for more complex cases, only_if may still be required.
|
||||
|
||||
Here are various examples of 'when' in use. 'when' is incompatible with 'only_if' in the same task::
|
||||
|
||||
- name: "do this if my favcolor is blue, and my dog is named fido"
|
||||
action: shell /bin/false
|
||||
when_string: $favcolor == 'blue' and $dog == 'fido'
|
||||
|
||||
- name: "do this if my favcolor is not blue, and my dog is named fido"
|
||||
action: shell /bin/true
|
||||
when_string: $favcolor != 'blue' and $dog == 'fido'
|
||||
|
||||
- name: "do this if my SSN is over 9000"
|
||||
action: shell /bin/true
|
||||
when_integer: $ssn > 9000
|
||||
|
||||
- name: "do this if I have one of these SSNs"
|
||||
action: shell /bin/true
|
||||
when_integer: $ssn in [ 8675309, 8675310, 8675311 ]
|
||||
|
||||
- name: "do this if a variable named hippo is NOT defined"
|
||||
action: shell /bin/true
|
||||
when_unset: $hippo
|
||||
|
||||
- name: "do this if a variable named hippo is defined"
|
||||
action: shell /bin/true
|
||||
when_set: $hippo
|
||||
|
||||
- name: "do this if a variable named hippo is true"
|
||||
action: shell /bin/true
|
||||
when_boolean: $hippo
|
||||
|
||||
The when_boolean check will look for variables that look to be true as well, such as the string 'True' or
|
||||
'true', non-zero numbers, and so on.
|
||||
|
||||
Conditional Imports
|
||||
```````````````````
|
||||
|
||||
|
@ -604,7 +645,7 @@ Fireball Mode
|
|||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
Paramiko's core connection types of 'local', 'paramiko', and 'ssh' are augmented in version 0.8 by a new extra-fast
|
||||
Ansible's core connection types of 'local', 'paramiko', and 'ssh' are augmented in version 0.8 and later by a new extra-fast
|
||||
connection type called 'fireball'. It can only be used with playbooks and does require some additional setup
|
||||
outside the lines of ansible's normal "no bootstrapping" philosophy. You are not required to use fireball mode
|
||||
to use Ansible, though some users may appreciate it.
|
||||
|
@ -654,7 +695,10 @@ any platform. You will also need gcc and zeromq-devel installed from your packa
|
|||
- PyCrypto
|
||||
- python-keyczar
|
||||
|
||||
For more information about fireball, see the module documentation section.
|
||||
Fedora and EPEL also have Ansible RPM subpackages available for fireball-dependencies.
|
||||
|
||||
Also see the module documentation section.
|
||||
|
||||
|
||||
Understanding Variable Precedence
|
||||
`````````````````````````````````
|
||||
|
|
|
@ -283,7 +283,8 @@ def main():
|
|||
fname = os.path.join(options.module_dir, module)
|
||||
extra = os.path.join("inc", "%s.tex" % module)
|
||||
|
||||
if fname.endswith(".swp"):
|
||||
# probably could just throw out everything with extensions
|
||||
if fname.endswith(".swp") or fname.endswith(".orig") or fname.endswith(".rej"):
|
||||
continue
|
||||
|
||||
print " processing module source ---> %s" % fname
|
||||
|
|
|
@ -308,13 +308,13 @@ class PlayBook(object):
|
|||
for host, results in results.get('contacted',{}).iteritems():
|
||||
if results.get('changed', False):
|
||||
for handler_name in task.notify:
|
||||
self._flag_handler(play.handlers(), utils.template(play.basedir, handler_name, task.module_vars), host)
|
||||
self._flag_handler(play, utils.template(play.basedir, handler_name, task.module_vars), host)
|
||||
|
||||
return hosts_remaining
|
||||
|
||||
# *****************************************************
|
||||
|
||||
def _flag_handler(self, handlers, handler_name, host):
|
||||
def _flag_handler(self, play, handler_name, host):
|
||||
'''
|
||||
if a task has any notify elements, flag handlers for run
|
||||
at end of execution cycle for hosts that have indicated
|
||||
|
@ -322,8 +322,8 @@ class PlayBook(object):
|
|||
'''
|
||||
|
||||
found = False
|
||||
for x in handlers:
|
||||
if handler_name == x.name:
|
||||
for x in play.handlers():
|
||||
if handler_name == utils.template(play.basedir, x.name, x.module_vars):
|
||||
found = True
|
||||
self.callbacks.on_notify(host, x.name)
|
||||
x.notified_by.append(host)
|
||||
|
|
|
@ -161,7 +161,7 @@ class Play(object):
|
|||
if type(self.vars) not in [dict, list]:
|
||||
raise errors.AnsibleError("'vars' section must contain only key/value pairs")
|
||||
|
||||
vars = self.playbook.global_vars
|
||||
vars = {}
|
||||
|
||||
# translate a list of vars into a dict
|
||||
if type(self.vars) == list:
|
||||
|
@ -187,12 +187,14 @@ class Play(object):
|
|||
salt_size = var.get("salt_size", None)
|
||||
salt = var.get("salt", None)
|
||||
|
||||
vars[vname] = self.playbook.callbacks.on_vars_prompt(vname, private, prompt,encrypt, confirm, salt_size, salt)
|
||||
if vname not in self.playbook.extra_vars:
|
||||
vars[vname] = self.playbook.callbacks.on_vars_prompt(vname, private, prompt,encrypt, confirm, salt_size, salt)
|
||||
|
||||
elif type(self.vars_prompt) == dict:
|
||||
for (vname, prompt) in self.vars_prompt.iteritems():
|
||||
prompt_msg = "%s: " % prompt
|
||||
vars[vname] = self.playbook.callbacks.on_vars_prompt(varname=vname, private=False, prompt=prompt_msg)
|
||||
if vname not in self.playbook.extra_vars:
|
||||
vars[vname] = self.playbook.callbacks.on_vars_prompt(varname=vname, private=False, prompt=prompt_msg)
|
||||
|
||||
else:
|
||||
raise errors.AnsibleError("'vars_prompt' section is malformed, see docs")
|
||||
|
|
|
@ -45,6 +45,8 @@ class Task(object):
|
|||
|
||||
# code to allow for saying "modulename: args" versus "action: modulename args"
|
||||
if x in utils.plugins.module_finder:
|
||||
if 'action' in ds:
|
||||
raise errors.AnsibleError("multiple actions specified in task %s" % (ds.get('name', ds['action'])))
|
||||
ds['action'] = x + " " + ds[x]
|
||||
ds.pop(x)
|
||||
|
||||
|
@ -149,9 +151,6 @@ class Task(object):
|
|||
# allow the user to list comma delimited tags
|
||||
import_tags = import_tags.split(",")
|
||||
|
||||
self.name = utils.template(None, self.name, self.module_vars)
|
||||
self.action = utils.template(None, self.action, self.module_vars)
|
||||
|
||||
# handle mutually incompatible options
|
||||
incompatibles = [ x for x in [ self.first_available_file, self.items_lookup_plugin ] if x is not None ]
|
||||
if len(incompatibles) > 1:
|
||||
|
@ -233,7 +232,7 @@ class Task(object):
|
|||
tcopy = tokens[1:]
|
||||
for (i, t) in enumerate(tcopy):
|
||||
if t.find("$") != -1:
|
||||
tcopy[i] = "(is_set('''%s''') and '''%s'''.lower() not in ('false', 'none', '0', ''))" % (t, t)
|
||||
tcopy[i] = "(is_set('''%s''') and '''%s'''.lower() not in ('false', 'no', 'n', 'none', '0', ''))" % (t, t)
|
||||
return " ".join(tcopy)
|
||||
|
||||
else:
|
||||
|
|
|
@ -72,7 +72,8 @@ examples:
|
|||
|
||||
import platform
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import shlex
|
||||
|
||||
class Service(object):
|
||||
"""
|
||||
|
@ -108,6 +109,9 @@ class Service(object):
|
|||
self.svc_initctl = None
|
||||
self.enable_cmd = None
|
||||
self.arguments = module.params.get('arguments', '')
|
||||
self.rcconf_file = None
|
||||
self.rcconf_key = None
|
||||
self.rcconf_value = None
|
||||
|
||||
# select whether we dump additional debug info through syslog
|
||||
self.syslogging = False
|
||||
|
@ -194,6 +198,59 @@ class Service(object):
|
|||
out = ''
|
||||
return rc, out, err
|
||||
|
||||
def service_enable_rcconf(self):
|
||||
if self.rcconf_file is None or self.rcconf_key is None or self.rcconf_value is None:
|
||||
self.module.fail_json(msg="service_enable_rcconf() requires rcconf_file, rcconf_key and rcconf_value")
|
||||
|
||||
changed = None
|
||||
entry = '%s="%s"\n' % (self.rcconf_key, self.rcconf_value)
|
||||
RCFILE = open(self.rcconf_file, "r")
|
||||
new_rc_conf = []
|
||||
|
||||
# Build a list containing the possibly modified file.
|
||||
for rcline in RCFILE:
|
||||
# Parse line removing whitespaces, quotes, etc.
|
||||
rcarray = shlex.split(rcline, comments=True)
|
||||
if len(rcarray) >= 1 and '=' in rcarray[0]:
|
||||
(key, value) = rcarray[0].split("=", 1)
|
||||
if key == self.rcconf_key:
|
||||
if value == self.rcconf_value:
|
||||
# Since the proper entry already exists we can stop iterating.
|
||||
changed = False
|
||||
break
|
||||
else:
|
||||
# We found the key but the value is wrong, replace with new entry.
|
||||
rcline = entry
|
||||
changed = True
|
||||
|
||||
# Add line to the list.
|
||||
new_rc_conf.append(rcline)
|
||||
|
||||
# We are done with reading the current rc.conf, close it.
|
||||
RCFILE.close()
|
||||
|
||||
# If we did not see any trace of our entry we need to add it.
|
||||
if changed is None:
|
||||
new_rc_conf.append(entry)
|
||||
changed = True
|
||||
|
||||
if changed is True:
|
||||
# Create a temporary file next to the current rc.conf (so we stay on the same filesystem).
|
||||
# This way the replacement operation is atomic.
|
||||
rcconf_dir = os.path.dirname(self.rcconf_file)
|
||||
rcconf_base = os.path.basename(self.rcconf_file)
|
||||
(TMP_RCCONF, tmp_rcconf_file) = tempfile.mkstemp(dir=rcconf_dir, prefix="%s-" % rcconf_base)
|
||||
|
||||
# Write out the contents of the list into our temporary file.
|
||||
for rcline in new_rc_conf:
|
||||
os.write(TMP_RCCONF, rcline)
|
||||
|
||||
# Close temporary file.
|
||||
os.close(TMP_RCCONF)
|
||||
|
||||
# Replace previous rc.conf.
|
||||
self.module.atomic_replace(tmp_rcconf_file, self.rcconf_file)
|
||||
|
||||
# ===========================================
|
||||
# Subclass: Linux
|
||||
|
||||
|
@ -368,28 +425,18 @@ class FreeBsdService(Service):
|
|||
|
||||
def service_enable(self):
|
||||
if self.enable:
|
||||
rc = "YES"
|
||||
self.rcconf_value = "YES"
|
||||
else:
|
||||
rc = "NO"
|
||||
self.rcconf_value = "NO"
|
||||
|
||||
rcfiles = [ '/etc/rc.conf','/usr/local/etc/rc.conf' ]
|
||||
for rcfile in rcfiles:
|
||||
if os.path.isfile(rcfile):
|
||||
rcconf = rcfile
|
||||
self.rcconf_file = rcfile
|
||||
|
||||
self.rcconf_key = "%s_enable" % self.name
|
||||
|
||||
entry = "%s_enable" % self.name
|
||||
full_entry = '%s="%s"' % (entry,rc)
|
||||
rc = open(rcconf,"r+")
|
||||
rctext = rc.read()
|
||||
if re.search("^%s" % full_entry,rctext,re.M) is None:
|
||||
if re.search("^%s" % entry,rctext,re.M) is None:
|
||||
rctext += "\n%s" % full_entry
|
||||
else:
|
||||
rctext = re.sub("^%s.*" % entry,full_entry,rctext,1,re.M)
|
||||
rc.truncate(0)
|
||||
rc.seek(0)
|
||||
rc.write(rctext)
|
||||
rc.close()
|
||||
return self.service_enable_rcconf()
|
||||
|
||||
def service_control(self):
|
||||
if self.action is "start":
|
||||
|
|
Loading…
Reference in a new issue