implement lookup plugins for arbitrary enumeration over arbitrary things. See the mailing list for some cool examples.

This commit is contained in:
Michael DeHaan
2012-10-12 20:07:05 -04:00
parent 29d49d415f
commit c5d2f6b0d3
14 changed files with 135 additions and 28 deletions

View File

@@ -47,7 +47,7 @@ except ImportError:
dirname = os.path.dirname(__file__)
action_plugin_list = utils.import_plugins(os.path.join(dirname, 'action_plugins'))
lookup_plugin_list = utils.import_plugins(os.path.join(dirname, 'lookup_plugins'))
################################################
@@ -71,8 +71,8 @@ def _executor_hook(job_queue, result_queue):
traceback.print_exc()
class HostVars(dict):
''' A special view of setup_cache that adds values from the inventory
when needed. '''
''' A special view of setup_cache that adds values from the inventory when needed. '''
def __init__(self, setup_cache, inventory):
self.setup_cache = setup_cache
self.inventory = inventory
@@ -82,9 +82,10 @@ class HostVars(dict):
def __getitem__(self, host):
if not host in self.lookup:
self.lookup[host] = self.inventory.get_variables(host)
self.setup_cache[host].update(self.lookup[host])
return self.setup_cache[host]
result = self.inventory.get_variables(host)
result.update(self.setup_cache.get(host, {}))
self.lookup[host] = result
return self.lookup[host]
class Runner(object):
''' core API interface to ansible '''
@@ -160,8 +161,11 @@ class Runner(object):
# instantiate plugin classes
self.action_plugins = {}
self.lookup_plugins = {}
for (k,v) in action_plugin_list.iteritems():
self.action_plugins[k] = v.ActionModule(self)
for (k,v) in lookup_plugin_list.iteritems():
self.lookup_plugins[k] = v.LookupModule(self)
# *****************************************************
@@ -189,7 +193,10 @@ class Runner(object):
afd, afile = tempfile.mkstemp()
afo = os.fdopen(afd, 'w')
afo.write(data.encode('utf8'))
try:
afo.write(data.encode('utf8'))
except:
raise errors.AnsibleError("failure encoding into utf-8")
afo.flush()
afo.close()
@@ -283,10 +290,18 @@ class Runner(object):
# allow with_items to work in playbooks...
# apt and yum are converted into a single call, others run in a loop
items = self.module_vars.get('items', [])
if isinstance(items, basestring) and items.startswith("$"):
items = utils.varReplaceWithItems(self.basedir, items, inject)
# if we instead said 'with_foo' and there is a lookup module named foo...
items_plugin = self.module_vars.get('items_lookup_plugin', None)
if items_plugin is not None:
items_terms = self.module_vars.get('items_lookup_terms', '')
if items_plugin in self.lookup_plugins:
items_terms = utils.template(self.basedir, items_terms, inject)
items = self.lookup_plugins[items_plugin].run(items_terms)
if type(items) != list:
raise errors.AnsibleError("with_items only takes a list: %s" % items)
@@ -313,6 +328,7 @@ class Runner(object):
results.append(result.result)
if result.comm_ok == False:
all_comm_ok = False
all_failed = True
break
for x in results:
if x.get('changed') == True:
@@ -320,7 +336,7 @@ class Runner(object):
if (x.get('failed') == True) or (('rc' in x) and (x['rc'] != 0)):
all_failed = True
break
msg = 'All items succeeded'
msg = 'All items completed'
if all_failed:
msg = "One or more items failed."
rd_result = dict(failed=all_failed, changed=all_changed, results=results, msg=msg)

View File

@@ -39,6 +39,11 @@ class ActionModule(object):
options = utils.parse_kv(module_args)
source = options.get('src', None)
dest = options.get('dest', None)
if dest.endswith("/"):
base = os.path.basename(source)
dest = os.path.join(dest, base)
if (source is None and not 'first_available_file' in inject) or dest is None:
result=dict(failed=True, msg="src and dest are required")
return ReturnData(conn=conn, result=result)
@@ -78,10 +83,16 @@ class ActionModule(object):
# run the copy module
module_args = "%s src=%s" % (module_args, tmp_src)
print "CALLING FILE WITH: %s" % module_args
return self.runner._execute_module(conn, tmp, 'copy', module_args, inject=inject).daisychain('file', module_args)
else:
# no need to transfer the file, already correct md5
# no need to transfer the file, already correct md5, but still need to set src so the file module
# does not freak out. It's just the basename of the file.
tmp_src = tmp + os.path.basename(source)
module_args = "%s src=%s" % (module_args, tmp_src)
result = dict(changed=False, md5sum=remote_md5, transferred=False)
print "CALLING FILE WITH: %s" % module_args
return ReturnData(conn=conn, result=result).daisychain('file', module_args)

View File

@@ -42,6 +42,11 @@ class ActionModule(object):
options = utils.parse_kv(module_args)
source = options.get('src', None)
dest = options.get('dest', None)
if dest.endswith("/"):
base = os.path.basename(source)
dest = os.path.join(dest, base)
if (source is None and 'first_available_file' not in inject) or dest is None:
result = dict(failed=True, msg="src and dest are required")
return ReturnData(conn=conn, comm_ok=False, result=result)

View File

@@ -0,0 +1,30 @@
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import os
import glob
class LookupModule(object):
def __init__(self, runner):
self.runner = runner
def run(self, terms):
return [ f for f in glob.glob(terms) if os.path.isfile(f) ]