Add assemble module
This adds a module that concatenates (ie. assembles) a file from fragments in a directory in alphabetical order. It chains the file module afterward to fix up ownership and permission. This also adds tests for the assemble module with fragments in assemble.d.
This commit is contained in:
parent
93ac6c5ba0
commit
9cf182c225
6 changed files with 158 additions and 0 deletions
|
@ -612,6 +612,20 @@ class Runner(object):
|
|||
|
||||
# *****************************************************
|
||||
|
||||
def _execute_assemble(self, conn, tmp):
|
||||
''' handler for assemble operations '''
|
||||
module_name = 'assemble'
|
||||
options = utils.parse_kv(self.module_args)
|
||||
module = self._transfer_module(conn, tmp, module_name)
|
||||
exec_rc = self._execute_module(conn, tmp, module, self.module_args)
|
||||
|
||||
if exec_rc.is_successful():
|
||||
return self._chain_file_module(conn, tmp, exec_rc, options)
|
||||
else:
|
||||
return exec_rc
|
||||
|
||||
# *****************************************************
|
||||
|
||||
def _executor(self, host):
|
||||
try:
|
||||
exec_rc = self._executor_internal(host)
|
||||
|
@ -656,6 +670,8 @@ class Runner(object):
|
|||
result = self._execute_template(conn, tmp)
|
||||
elif self.module_name == 'raw':
|
||||
result = self._execute_raw(conn, tmp)
|
||||
elif self.module_name == 'assemble':
|
||||
result = self._execute_assemble(conn, tmp)
|
||||
else:
|
||||
if self.background == 0:
|
||||
result = self._execute_normal_module(conn, tmp, module_name)
|
||||
|
|
111
library/assemble
Executable file
111
library/assemble
Executable file
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# (c) 2012, Stephen Fromm <sfromm@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/>.
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
import os
|
||||
import os.path
|
||||
import md5
|
||||
import sys
|
||||
import shlex
|
||||
import shutil
|
||||
import syslog
|
||||
import tempfile
|
||||
|
||||
# Since hashlib is only available in 2.5 and onwards, this module
|
||||
# uses md5 which is available in 2.4.
|
||||
|
||||
# ===========================================
|
||||
# Support methods
|
||||
|
||||
def exit_json(rc=0, **kwargs):
|
||||
print json.dumps(kwargs)
|
||||
sys.exit(rc)
|
||||
|
||||
def fail_json(**kwargs):
|
||||
kwargs['failed'] = True
|
||||
exit_json(rc=1, **kwargs)
|
||||
|
||||
def assemble_from_fragments(path):
|
||||
''' assemble a file from a directory of fragments '''
|
||||
assembled = []
|
||||
for f in sorted(os.listdir(path)):
|
||||
fragment = "%s/%s" % (path, f)
|
||||
if os.path.isfile(fragment):
|
||||
assembled.append(file(fragment).read())
|
||||
return "".join(assembled)
|
||||
|
||||
def write_temp_file(data):
|
||||
fd, path = tempfile.mkstemp()
|
||||
os.write(fd, data)
|
||||
os.close(fd)
|
||||
return path
|
||||
|
||||
# ===========================================
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
fail_json(msg="the assemble module requires arguments (-a)")
|
||||
|
||||
argfile = sys.argv[1]
|
||||
if not os.path.exists(argfile):
|
||||
fail_json(msg="Argument file not found")
|
||||
|
||||
args = open(argfile, 'r').read()
|
||||
items = shlex.split(args)
|
||||
syslog.openlog('ansible-%s' % os.path.basename(__file__))
|
||||
syslog.syslog(syslog.LOG_NOTICE, 'Invoked with %s' % args)
|
||||
|
||||
if not len(items):
|
||||
fail_json(msg="the assemble module requires arguments (-a)")
|
||||
|
||||
params = {}
|
||||
for x in items:
|
||||
(k, v) = x.split("=")
|
||||
params[k] = v
|
||||
|
||||
changed = False
|
||||
pathmd5 = None
|
||||
destmd5 = None
|
||||
src = params.get('src', None)
|
||||
dest = params.get('dest', None)
|
||||
|
||||
if src:
|
||||
src = os.path.expanduser(src)
|
||||
if dest:
|
||||
dest = os.path.expanduser(dest)
|
||||
|
||||
if not os.path.exists(src):
|
||||
fail_json(msg="Source (%s) does not exist" % src)
|
||||
|
||||
if not os.path.isdir(src):
|
||||
fail_json(msg="Source (%s) is not a directory" % src)
|
||||
|
||||
path = write_temp_file(assemble_from_fragments(src))
|
||||
pathmd5 = md5.new(file(path).read()).hexdigest()
|
||||
|
||||
if os.path.exists(dest):
|
||||
destmd5 = md5.new(file(dest).read()).hexdigest()
|
||||
|
||||
if pathmd5 != destmd5:
|
||||
shutil.copy(path, dest)
|
||||
changed = True
|
||||
|
||||
exit_json(md5sum=pathmd5, changed=changed)
|
|
@ -231,5 +231,28 @@ class TestRunner(unittest.TestCase):
|
|||
def test_service(self):
|
||||
# TODO: tests for the service module
|
||||
pass
|
||||
def test_assemble(self):
|
||||
input = self._get_test_file('assemble.d')
|
||||
metadata = self._get_test_file('metadata.json')
|
||||
output = self._get_stage_file('sample.out')
|
||||
result = self._run('assemble', [
|
||||
"src=%s" % input,
|
||||
"dest=%s" % output,
|
||||
"metadata=%s" % metadata
|
||||
])
|
||||
assert os.path.exists(output)
|
||||
out = file(output).read()
|
||||
assert out.find("first") != -1
|
||||
assert out.find("second") != -1
|
||||
assert out.find("third") != -1
|
||||
assert result['changed'] == True
|
||||
assert 'md5sum' in result
|
||||
assert 'failed' not in result
|
||||
result = self._run('assemble', [
|
||||
"src=%s" % input,
|
||||
"dest=%s" % output,
|
||||
"metadata=%s" % metadata
|
||||
])
|
||||
assert result['changed'] == False
|
||||
|
||||
|
||||
|
|
2
test/assemble.d/00-source.txt
Normal file
2
test/assemble.d/00-source.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
# This is the first comment.
|
||||
[somegroup]
|
2
test/assemble.d/01-source.txt
Normal file
2
test/assemble.d/01-source.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
# This is the second comment.
|
||||
127.0.0.2
|
4
test/assemble.d/02.source.txt
Normal file
4
test/assemble.d/02.source.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
# This is the third comment.
|
||||
[somegroup:vars]
|
||||
foo=bar
|
||||
# 無 可 無 非 常 無
|
Loading…
Reference in a new issue