mirror of https://github.com/ansible/ansible.git
Moving more action plugins over and fixing some bugs with role loading
parent
7f7e9914aa
commit
065733ad93
@ -0,0 +1,62 @@
|
|||||||
|
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||||
|
# Copyright 2012, Seth Vidal <skvidal@fedoraproject.org>
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
''' Create inventory hosts and groups in the memory inventory'''
|
||||||
|
|
||||||
|
### We need to be able to modify the inventory
|
||||||
|
BYPASS_HOST_LOOP = True
|
||||||
|
TRANSFERS_FILES = False
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=dict()):
|
||||||
|
|
||||||
|
# FIXME: is this necessary in v2?
|
||||||
|
#if self.runner.noop_on_check(inject):
|
||||||
|
# return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True, msg='check mode not supported for this module'))
|
||||||
|
|
||||||
|
# Parse out any hostname:port patterns
|
||||||
|
new_name = self._task.args.get('name', self._task.args.get('hostname', None))
|
||||||
|
#vv("creating host via 'add_host': hostname=%s" % new_name)
|
||||||
|
|
||||||
|
if ":" in new_name:
|
||||||
|
new_name, new_port = new_name.split(":")
|
||||||
|
self._task.args['ansible_ssh_port'] = new_port
|
||||||
|
|
||||||
|
groups = self._task.args.get('groupname', self._task.args.get('groups', self._task.args.get('group', '')))
|
||||||
|
# add it to the group if that was specified
|
||||||
|
new_groups = []
|
||||||
|
if groups:
|
||||||
|
for group_name in groups.split(","):
|
||||||
|
if group_name not in new_groups:
|
||||||
|
new_groups.append(group_name.strip())
|
||||||
|
|
||||||
|
# Add any variables to the new_host
|
||||||
|
host_vars = dict()
|
||||||
|
for k in self._task.args.keys():
|
||||||
|
if not k in [ 'name', 'hostname', 'groupname', 'groups' ]:
|
||||||
|
host_vars[k] = self._task.args[k]
|
||||||
|
|
||||||
|
return dict(changed=True, add_host=dict(host_name=new_name, groups=new_groups, host_vars=host_vars))
|
||||||
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
|||||||
|
# (c) 2012-2014, 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 json
|
||||||
|
import random
|
||||||
|
|
||||||
|
from ansible import constants as C
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=dict()):
|
||||||
|
''' transfer the given module name, plus the async module, then run it '''
|
||||||
|
|
||||||
|
# FIXME: noop stuff needs to be sorted ut
|
||||||
|
#if self.runner.noop_on_check(inject):
|
||||||
|
# return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True, msg='check mode not supported for this module'))
|
||||||
|
|
||||||
|
if not tmp:
|
||||||
|
tmp = self._make_tmp_path()
|
||||||
|
|
||||||
|
module_name = self._task.action
|
||||||
|
async_module_path = self._shell.join_path(tmp, 'async_wrapper')
|
||||||
|
remote_module_path = self._shell.join_path(tmp, module_name)
|
||||||
|
|
||||||
|
env_string = self._compute_environment_string()
|
||||||
|
|
||||||
|
# configure, upload, and chmod the target module
|
||||||
|
(module_style, shebang, module_data) = self._configure_module(module_name=module_name, module_args=self._task.args)
|
||||||
|
self._transfer_data(remote_module_path, module_data)
|
||||||
|
self._remote_chmod(tmp, 'a+rx', remote_module_path)
|
||||||
|
|
||||||
|
# configure, upload, and chmod the async_wrapper module
|
||||||
|
(async_module_style, shebang, async_module_data) = self._configure_module(module_name='async_wrapper', module_args=dict())
|
||||||
|
self._transfer_data(async_module_path, async_module_data)
|
||||||
|
self._remote_chmod(tmp, 'a+rx', async_module_path)
|
||||||
|
|
||||||
|
argsfile = self._transfer_data(self._shell.join_path(tmp, 'arguments'), json.dumps(self._task.args))
|
||||||
|
|
||||||
|
async_limit = self._task.async
|
||||||
|
async_jid = str(random.randint(0, 999999999999))
|
||||||
|
|
||||||
|
async_cmd = " ".join([str(x) for x in [async_module_path, async_jid, async_limit, remote_module_path, argsfile]])
|
||||||
|
result = self._low_level_execute_command(cmd=async_cmd, tmp=None)
|
||||||
|
|
||||||
|
# clean up after
|
||||||
|
if tmp and "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES:
|
||||||
|
self._remove_tmp_path(tmp)
|
||||||
|
|
||||||
|
result['changed'] = True
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
|||||||
|
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||||
|
# (c) 2012, Dag Wieers <dag@wieers.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/>.
|
||||||
|
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
''' Fail with custom message '''
|
||||||
|
|
||||||
|
TRANSFERS_FILES = False
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=dict()):
|
||||||
|
|
||||||
|
msg = 'Failed as requested from task'
|
||||||
|
if self._task.args and 'msg' in self._task.args:
|
||||||
|
msg = self._task.args.get('msg')
|
||||||
|
|
||||||
|
return dict(failed=True, msg=msg)
|
||||||
|
|
@ -0,0 +1,152 @@
|
|||||||
|
# (c) 2012-2014, 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 pwd
|
||||||
|
import random
|
||||||
|
import traceback
|
||||||
|
import tempfile
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from ansible import constants as C
|
||||||
|
from ansible.errors import *
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
from ansible.utils.boolean import boolean
|
||||||
|
from ansible.utils.hashing import checksum, checksum_s, md5, secure_hash
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=dict()):
|
||||||
|
''' handler for fetch operations '''
|
||||||
|
|
||||||
|
# FIXME: is this even required anymore?
|
||||||
|
#if self.runner.noop_on_check(inject):
|
||||||
|
# return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True, msg='check mode not (yet) supported for this module'))
|
||||||
|
|
||||||
|
source = self._task.args.get('src', None)
|
||||||
|
dest = self._task.args.get('dest', None)
|
||||||
|
flat = boolean(self._task.args.get('flat'))
|
||||||
|
fail_on_missing = boolean(self._task.args.get('fail_on_missing'))
|
||||||
|
validate_checksum = boolean(self._task.args.get('validate_checksum', self._task.args.get('validate_md5')))
|
||||||
|
|
||||||
|
if 'validate_md5' in self._task.args and 'validate_checksum' in self._task.args:
|
||||||
|
return dict(failed=True, msg="validate_checksum and validate_md5 cannot both be specified")
|
||||||
|
|
||||||
|
if source is None or dest is None:
|
||||||
|
return dict(failed=True, msg="src and dest are required")
|
||||||
|
|
||||||
|
source = self._shell.join_path(source)
|
||||||
|
source = self._remote_expand_user(source, tmp)
|
||||||
|
|
||||||
|
# calculate checksum for the remote file
|
||||||
|
remote_checksum = self._remote_checksum(tmp, source)
|
||||||
|
|
||||||
|
# use slurp if sudo and permissions are lacking
|
||||||
|
remote_data = None
|
||||||
|
if remote_checksum in ('1', '2') or self._connection_info.sudo:
|
||||||
|
slurpres = self._execute_module(module_name='slurp', module_args=dict(src=source), tmp=tmp)
|
||||||
|
if slurpres.get('rc') == 0:
|
||||||
|
if slurpres['encoding'] == 'base64':
|
||||||
|
remote_data = base64.b64decode(slurpres['content'])
|
||||||
|
if remote_data is not None:
|
||||||
|
remote_checksum = checksum_s(remote_data)
|
||||||
|
# the source path may have been expanded on the
|
||||||
|
# target system, so we compare it here and use the
|
||||||
|
# expanded version if it's different
|
||||||
|
remote_source = slurpres.get('source')
|
||||||
|
if remote_source and remote_source != source:
|
||||||
|
source = remote_source
|
||||||
|
else:
|
||||||
|
# FIXME: should raise an error here? the old code did nothing
|
||||||
|
pass
|
||||||
|
|
||||||
|
# calculate the destination name
|
||||||
|
if os.path.sep not in self._shell.join_path('a', ''):
|
||||||
|
source_local = source.replace('\\', '/')
|
||||||
|
else:
|
||||||
|
source_local = source
|
||||||
|
|
||||||
|
dest = os.path.expanduser(dest)
|
||||||
|
if flat:
|
||||||
|
if dest.endswith("/"):
|
||||||
|
# if the path ends with "/", we'll use the source filename as the
|
||||||
|
# destination filename
|
||||||
|
base = os.path.basename(source_local)
|
||||||
|
dest = os.path.join(dest, base)
|
||||||
|
if not dest.startswith("/"):
|
||||||
|
# if dest does not start with "/", we'll assume a relative path
|
||||||
|
dest = self._loader.path_dwim(dest)
|
||||||
|
else:
|
||||||
|
# files are saved in dest dir, with a subdir for each host, then the filename
|
||||||
|
dest = "%s/%s/%s" % (self._loader.path_dwim(dest), self._connection._host, source_local)
|
||||||
|
|
||||||
|
dest = dest.replace("//","/")
|
||||||
|
|
||||||
|
if remote_checksum in ('0', '1', '2', '3', '4'):
|
||||||
|
# these don't fail because you may want to transfer a log file that possibly MAY exist
|
||||||
|
# but keep going to fetch other log files
|
||||||
|
if remote_checksum == '0':
|
||||||
|
result = dict(msg="unable to calculate the checksum of the remote file", file=source, changed=False)
|
||||||
|
elif remote_checksum == '1':
|
||||||
|
if fail_on_missing:
|
||||||
|
result = dict(failed=True, msg="the remote file does not exist", file=source)
|
||||||
|
else:
|
||||||
|
result = dict(msg="the remote file does not exist, not transferring, ignored", file=source, changed=False)
|
||||||
|
elif remote_checksum == '2':
|
||||||
|
result = dict(msg="no read permission on remote file, not transferring, ignored", file=source, changed=False)
|
||||||
|
elif remote_checksum == '3':
|
||||||
|
result = dict(msg="remote file is a directory, fetch cannot work on directories", file=source, changed=False)
|
||||||
|
elif remote_checksum == '4':
|
||||||
|
result = dict(msg="python isn't present on the system. Unable to compute checksum", file=source, changed=False)
|
||||||
|
return result
|
||||||
|
|
||||||
|
# calculate checksum for the local file
|
||||||
|
local_checksum = checksum(dest)
|
||||||
|
|
||||||
|
if remote_checksum != local_checksum:
|
||||||
|
# create the containing directories, if needed
|
||||||
|
if not os.path.isdir(os.path.dirname(dest)):
|
||||||
|
os.makedirs(os.path.dirname(dest))
|
||||||
|
|
||||||
|
# fetch the file and check for changes
|
||||||
|
if remote_data is None:
|
||||||
|
self._connection.fetch_file(source, dest)
|
||||||
|
else:
|
||||||
|
f = open(dest, 'w')
|
||||||
|
f.write(remote_data)
|
||||||
|
f.close()
|
||||||
|
new_checksum = secure_hash(dest)
|
||||||
|
# For backwards compatibility. We'll return None on FIPS enabled
|
||||||
|
# systems
|
||||||
|
try:
|
||||||
|
new_md5 = md5(dest)
|
||||||
|
except ValueError:
|
||||||
|
new_md5 = None
|
||||||
|
|
||||||
|
if validate_checksum and new_checksum != remote_checksum:
|
||||||
|
return dict(failed=True, md5sum=new_md5, msg="checksum mismatch", file=source, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum)
|
||||||
|
return dict(changed=True, md5sum=new_md5, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum)
|
||||||
|
else:
|
||||||
|
# For backwards compatibility. We'll return None on FIPS enabled
|
||||||
|
# systems
|
||||||
|
try:
|
||||||
|
local_md5 = md5(dest)
|
||||||
|
except ValueError:
|
||||||
|
local_md5 = None
|
||||||
|
|
||||||
|
return dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum)
|
||||||
|
|
@ -0,0 +1,37 @@
|
|||||||
|
# Copyright 2012, Jeroen Hoekx <jeroen@hoekx.be>
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
from ansible.errors import *
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
''' Create inventory groups based on variables '''
|
||||||
|
|
||||||
|
### We need to be able to modify the inventory
|
||||||
|
BYPASS_HOST_LOOP = True
|
||||||
|
TRANSFERS_FILES = False
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=dict()):
|
||||||
|
|
||||||
|
if not 'key' in self._task.args:
|
||||||
|
return dict(failed=True, msg="the 'key' param is required when using group_by")
|
||||||
|
|
||||||
|
group_name = self._task.args.get('key')
|
||||||
|
group_name = group_name.replace(' ','-')
|
||||||
|
|
||||||
|
return dict(changed=True, add_group=group_name)
|
||||||
|
|
@ -0,0 +1,134 @@
|
|||||||
|
# Copyright 2012, Tim Bielawa <tbielawa@redhat.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 datetime
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from termios import tcflush, TCIFLUSH
|
||||||
|
|
||||||
|
from ansible.errors import *
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
''' pauses execution for a length or time, or until input is received '''
|
||||||
|
|
||||||
|
PAUSE_TYPES = ['seconds', 'minutes', 'prompt', '']
|
||||||
|
BYPASS_HOST_LOOP = True
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=dict()):
|
||||||
|
''' run the pause action module '''
|
||||||
|
|
||||||
|
duration_unit = 'minutes'
|
||||||
|
prompt = None
|
||||||
|
seconds = None
|
||||||
|
result = dict(
|
||||||
|
changed = False,
|
||||||
|
rc = 0,
|
||||||
|
stderr = '',
|
||||||
|
stdout = '',
|
||||||
|
start = None,
|
||||||
|
stop = None,
|
||||||
|
delta = None,
|
||||||
|
)
|
||||||
|
|
||||||
|
# FIXME: not sure if we can get this info directly like this anymore?
|
||||||
|
#hosts = ', '.join(self.runner.host_set)
|
||||||
|
|
||||||
|
# Is 'args' empty, then this is the default prompted pause
|
||||||
|
if self._task.args is None or len(self._task.args.keys()) == 0:
|
||||||
|
pause_type = 'prompt'
|
||||||
|
#prompt = "[%s]\nPress enter to continue:\n" % hosts
|
||||||
|
prompt = "[%s]\nPress enter to continue:\n" % self._task.get_name().strip()
|
||||||
|
|
||||||
|
# Are 'minutes' or 'seconds' keys that exist in 'args'?
|
||||||
|
elif 'minutes' in self._task.args or 'seconds' in self._task.args:
|
||||||
|
try:
|
||||||
|
if 'minutes' in self._task.args:
|
||||||
|
pause_type = 'minutes'
|
||||||
|
# The time() command operates in seconds so we need to
|
||||||
|
# recalculate for minutes=X values.
|
||||||
|
seconds = int(self._task.args['minutes']) * 60
|
||||||
|
else:
|
||||||
|
pause_type = 'seconds'
|
||||||
|
seconds = int(self._task.args['seconds'])
|
||||||
|
duration_unit = 'seconds'
|
||||||
|
|
||||||
|
except ValueError, e:
|
||||||
|
return dict(failed=True, msg="non-integer value given for prompt duration:\n%s" % str(e))
|
||||||
|
|
||||||
|
# Is 'prompt' a key in 'args'?
|
||||||
|
elif 'prompt' in self._task.args:
|
||||||
|
pause_type = 'prompt'
|
||||||
|
#prompt = "[%s]\n%s:\n" % (hosts, self._task.args['prompt'])
|
||||||
|
prompt = "[%s]\n%s:\n" % (self._task.get_name().strip(), self._task.args['prompt'])
|
||||||
|
|
||||||
|
# I have no idea what you're trying to do. But it's so wrong.
|
||||||
|
else:
|
||||||
|
return dict(failed=True, msg="invalid pause type given. must be one of: %s" % ", ".join(self.PAUSE_TYPES))
|
||||||
|
|
||||||
|
#vv("created 'pause' ActionModule: pause_type=%s, duration_unit=%s, calculated_seconds=%s, prompt=%s" % \
|
||||||
|
# (self.pause_type, self.duration_unit, self.seconds, self.prompt))
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Begin the hard work!
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
result['start'] = str(datetime.datetime.now())
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME: this is all very broken right now, as prompting from the worker side
|
||||||
|
# is not really going to be supported, and actions marked as BYPASS_HOST_LOOP
|
||||||
|
# probably should not be run through the executor engine at all. Also, ctrl+c
|
||||||
|
# is now captured on the parent thread, so it can't be caught here via the
|
||||||
|
# KeyboardInterrupt exception.
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not pause_type == 'prompt':
|
||||||
|
print "(^C-c = continue early, ^C-a = abort)"
|
||||||
|
#print("[%s]\nPausing for %s seconds" % (hosts, seconds))
|
||||||
|
print("[%s]\nPausing for %s seconds" % (self._task.get_name().strip(), seconds))
|
||||||
|
time.sleep(seconds)
|
||||||
|
else:
|
||||||
|
# Clear out any unflushed buffered input which would
|
||||||
|
# otherwise be consumed by raw_input() prematurely.
|
||||||
|
#tcflush(sys.stdin, TCIFLUSH)
|
||||||
|
result['user_input'] = raw_input(prompt.encode(sys.stdout.encoding))
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
while True:
|
||||||
|
print '\nAction? (a)bort/(c)ontinue: '
|
||||||
|
c = getch()
|
||||||
|
if c == 'c':
|
||||||
|
# continue playbook evaluation
|
||||||
|
break
|
||||||
|
elif c == 'a':
|
||||||
|
# abort further playbook evaluation
|
||||||
|
raise ae('user requested abort!')
|
||||||
|
finally:
|
||||||
|
duration = time.time() - start
|
||||||
|
result['stop'] = str(datetime.datetime.now())
|
||||||
|
result['delta'] = int(duration)
|
||||||
|
|
||||||
|
if duration_unit == 'minutes':
|
||||||
|
duration = round(duration / 60.0, 2)
|
||||||
|
else:
|
||||||
|
duration = round(duration, 2)
|
||||||
|
|
||||||
|
result['stdout'] = "Paused for %s %s" % (duration, duration_unit)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
# (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/>.
|
||||||
|
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
TRANSFERS_FILES = False
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=dict()):
|
||||||
|
|
||||||
|
# FIXME: need to rework the noop stuff still
|
||||||
|
#if self.runner.noop_on_check(inject):
|
||||||
|
# # in --check mode, always skip this module execution
|
||||||
|
# return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True))
|
||||||
|
|
||||||
|
executable = self._task.args.get('executable')
|
||||||
|
result = self._low_level_execute_command(self._task.args.get('_raw_params'), tmp=tmp, executable=executable)
|
||||||
|
|
||||||
|
# for some modules (script, raw), the sudo success key
|
||||||
|
# may leak into the stdout due to the way the sudo/su
|
||||||
|
# command is constructed, so we filter that out here
|
||||||
|
if result.get('stdout','').strip().startswith('SUDO-SUCCESS-'):
|
||||||
|
result['stdout'] = re.sub(r'^((\r)?\n)?SUDO-SUCCESS.*(\r)?\n', '', result['stdout'])
|
||||||
|
|
||||||
|
return result
|
@ -0,0 +1,99 @@
|
|||||||
|
# (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
|
||||||
|
|
||||||
|
from ansible import constants as C
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
TRANSFERS_FILES = True
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=None):
|
||||||
|
''' handler for file transfer operations '''
|
||||||
|
|
||||||
|
# FIXME: noop stuff still needs to be sorted out
|
||||||
|
#if self.runner.noop_on_check(inject):
|
||||||
|
# # in check mode, always skip this module
|
||||||
|
# return ReturnData(conn=conn, comm_ok=True,
|
||||||
|
# result=dict(skipped=True, msg='check mode not supported for this module'))
|
||||||
|
|
||||||
|
if not tmp:
|
||||||
|
tmp = self._make_tmp_path()
|
||||||
|
|
||||||
|
creates = self._task.args.get('creates')
|
||||||
|
if creates:
|
||||||
|
# do not run the command if the line contains creates=filename
|
||||||
|
# and the filename already exists. This allows idempotence
|
||||||
|
# of command executions.
|
||||||
|
result = self._execute_module(module_name='stat', module_args=dict(path=creates), tmp=tmp, persist_files=True)
|
||||||
|
stat = result.get('stat', None)
|
||||||
|
if stat and stat.get('exists', False):
|
||||||
|
return dict(skipped=True, msg=("skipped, since %s exists" % creates))
|
||||||
|
|
||||||
|
removes = self._task.args.get('removes')
|
||||||
|
if removes:
|
||||||
|
# do not run the command if the line contains removes=filename
|
||||||
|
# and the filename does not exist. This allows idempotence
|
||||||
|
# of command executions.
|
||||||
|
result = self._execute_module(module_name='stat', module_args=dict(path=removes), tmp=tmp, persist_files=True)
|
||||||
|
stat = result.get('stat', None)
|
||||||
|
if stat and not stat.get('exists', False):
|
||||||
|
return dict(skipped=True, msg=("skipped, since %s does not exist" % removes))
|
||||||
|
|
||||||
|
# the script name is the first item in the raw params, so we split it
|
||||||
|
# out now so we know the file name we need to transfer to the remote,
|
||||||
|
# and everything else is an argument to the script which we need later
|
||||||
|
# to append to the remote command
|
||||||
|
parts = self._task.args.get('_raw_params', '').strip().split()
|
||||||
|
source = parts[0]
|
||||||
|
args = ' '.join(parts[1:])
|
||||||
|
|
||||||
|
# FIXME: need to sort out all the _original_file stuff still
|
||||||
|
#if '_original_file' in task_vars:
|
||||||
|
# source = self._loader.path_dwim_relative(inject['_original_file'], 'files', source, self.runner.basedir)
|
||||||
|
#else:
|
||||||
|
# source = self._loader.path_dwim(self.runner.basedir, source)
|
||||||
|
source = self._loader.path_dwim(source)
|
||||||
|
|
||||||
|
# transfer the file to a remote tmp location
|
||||||
|
tmp_src = self._shell.join_path(tmp, os.path.basename(source))
|
||||||
|
self._connection.put_file(source, tmp_src)
|
||||||
|
|
||||||
|
sudoable = True
|
||||||
|
# set file permissions, more permissive when the copy is done as a different user
|
||||||
|
if ((self._connection_info.sudo and self._connection_info.sudo_user != 'root') or
|
||||||
|
(self._connection_info.su and self._connection_info.su_user != 'root')):
|
||||||
|
chmod_mode = 'a+rx'
|
||||||
|
sudoable = False
|
||||||
|
else:
|
||||||
|
chmod_mode = '+rx'
|
||||||
|
self._remote_chmod(tmp, chmod_mode, tmp_src, sudoable=sudoable)
|
||||||
|
|
||||||
|
# add preparation steps to one ssh roundtrip executing the script
|
||||||
|
env_string = self._compute_environment_string()
|
||||||
|
script_cmd = ' '.join([env_string, tmp_src, args])
|
||||||
|
|
||||||
|
result = self._low_level_execute_command(cmd=script_cmd, tmp=None, sudoable=sudoable)
|
||||||
|
|
||||||
|
# clean up after
|
||||||
|
if tmp and "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES:
|
||||||
|
self._remove_tmp_path(tmp)
|
||||||
|
|
||||||
|
result['changed'] = True
|
||||||
|
|
||||||
|
return result
|
@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# (c) 2012-2013, Timothy Appnel <tim@appnel.com>
|
||||||
|
#
|
||||||
|
# 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.path
|
||||||
|
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
from ansible.utils.boolean import boolean
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
|
||||||
|
def _get_absolute_path(self, path, task_vars):
|
||||||
|
if 'vars' in task_vars:
|
||||||
|
if '_original_file' in task_vars['vars']:
|
||||||
|
# roles
|
||||||
|
original_path = path
|
||||||
|
path = self._loader.path_dwim_relative(task_vars['_original_file'], 'files', path, self.runner.basedir)
|
||||||
|
if original_path and original_path[-1] == '/' and path[-1] != '/':
|
||||||
|
# make sure the dwim'd path ends in a trailing "/"
|
||||||
|
# if the original path did
|
||||||
|
path += '/'
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
def _process_origin(self, host, path, user, task_vars):
|
||||||
|
|
||||||
|
if not host in ['127.0.0.1', 'localhost']:
|
||||||
|
if user:
|
||||||
|
return '%s@%s:%s' % (user, host, path)
|
||||||
|
else:
|
||||||
|
return '%s:%s' % (host, path)
|
||||||
|
else:
|
||||||
|
if not ':' in path:
|
||||||
|
if not path.startswith('/'):
|
||||||
|
path = self._get_absolute_path(path=path, task_vars=task_vars)
|
||||||
|
return path
|
||||||
|
|
||||||
|
def _process_remote(self, host, path, user, task_vars):
|
||||||
|
transport = self._connection_info.connection
|
||||||
|
return_data = None
|
||||||
|
if not host in ['127.0.0.1', 'localhost'] or transport != "local":
|
||||||
|
if user:
|
||||||
|
return_data = '%s@%s:%s' % (user, host, path)
|
||||||
|
else:
|
||||||
|
return_data = '%s:%s' % (host, path)
|
||||||
|
else:
|
||||||
|
return_data = path
|
||||||
|
|
||||||
|
if not ':' in return_data:
|
||||||
|
if not return_data.startswith('/'):
|
||||||
|
return_data = self._get_absolute_path(path=return_data, task_vars=task_vars)
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=dict()):
|
||||||
|
''' generates params and passes them on to the rsync module '''
|
||||||
|
|
||||||
|
original_transport = task_vars.get('ansible_connection', self._connection_info.connection)
|
||||||
|
transport_overridden = False
|
||||||
|
if task_vars.get('delegate_to') is None:
|
||||||
|
task_vars['delegate_to'] = '127.0.0.1'
|
||||||
|
# IF original transport is not local, override transport and disable sudo.
|
||||||
|
if original_transport != 'local':
|
||||||
|
task_vars['ansible_connection'] = 'local'
|
||||||
|
self.transport_overridden = True
|
||||||
|
self.runner.sudo = False
|
||||||
|
|
||||||
|
src = self._task.args.get('src', None)
|
||||||
|
dest = self._task.args.get('dest', None)
|
||||||
|
|
||||||
|
# FIXME: this doesn't appear to be used anywhere?
|
||||||
|
local_rsync_path = task_vars.get('ansible_rsync_path')
|
||||||
|
|
||||||
|
# from the perspective of the rsync call the delegate is the localhost
|
||||||
|
src_host = '127.0.0.1'
|
||||||
|
dest_host = task_vars.get('ansible_ssh_host', task_vars.get('inventory_hostname'))
|
||||||
|
|
||||||
|
# allow ansible_ssh_host to be templated
|
||||||
|
# FIXME: does this still need to be templated?
|
||||||
|
#dest_host = template.template(self.runner.basedir, dest_host, task_vars, fail_on_undefined=True)
|
||||||
|
dest_is_local = dest_host in ['127.0.0.1', 'localhost']
|
||||||
|
|
||||||
|
# CHECK FOR NON-DEFAULT SSH PORT
|
||||||
|
dest_port = self._task.args.get('dest_port')
|
||||||
|
inv_port = task_vars.get('ansible_ssh_port', task_vars.get('inventory_hostname'))
|
||||||
|
if inv_port != dest_port and inv_port != task_vars.get('inventory_hostname'):
|
||||||
|
dest_port = inv_port
|
||||||
|
|
||||||
|
# edge case: explicit delegate and dest_host are the same
|
||||||
|
if dest_host == task_vars.get('delegate_to'):
|
||||||
|
dest_host = '127.0.0.1'
|
||||||
|
|
||||||
|
# SWITCH SRC AND DEST PER MODE
|
||||||
|
if self._task.args.get('mode', 'push') == 'pull':
|
||||||
|
(dest_host, src_host) = (src_host, dest_host)
|
||||||
|
|
||||||
|
# CHECK DELEGATE HOST INFO
|
||||||
|
use_delegate = False
|
||||||
|
# FIXME: not sure if this is in connection info yet or not...
|
||||||
|
#if conn.delegate != conn.host:
|
||||||
|
# if 'hostvars' in task_vars:
|
||||||
|
# if conn.delegate in task_vars['hostvars'] and self.original_transport != 'local':
|
||||||
|
# # use a delegate host instead of localhost
|
||||||
|
# use_delegate = True
|
||||||
|
|
||||||
|
# COMPARE DELEGATE, HOST AND TRANSPORT
|
||||||
|
process_args = False
|
||||||
|
if not dest_host is src_host and self.original_transport != 'local':
|
||||||
|
# interpret and task_vars remote host info into src or dest
|
||||||
|
process_args = True
|
||||||
|
|
||||||
|
# MUNGE SRC AND DEST PER REMOTE_HOST INFO
|
||||||
|
if process_args or use_delegate:
|
||||||
|
|
||||||
|
user = None
|
||||||
|
if boolean(options.get('set_remote_user', 'yes')):
|
||||||
|
if use_delegate:
|
||||||
|
user = task_vars['hostvars'][conn.delegate].get('ansible_ssh_user')
|
||||||
|
|
||||||
|
if not use_delegate or not user:
|
||||||
|
user = task_vars.get('ansible_ssh_user', self.runner.remote_user)
|
||||||
|
|
||||||
|
if use_delegate:
|
||||||
|
# FIXME
|
||||||
|
private_key = task_vars.get('ansible_ssh_private_key_file', self.runner.private_key_file)
|
||||||
|
else:
|
||||||
|
private_key = task_vars.get('ansible_ssh_private_key_file', self.runner.private_key_file)
|
||||||
|
|
||||||
|
private_key = template.template(self.runner.basedir, private_key, task_vars, fail_on_undefined=True)
|
||||||
|
|
||||||
|
if not private_key is None:
|
||||||
|
private_key = os.path.expanduser(private_key)
|
||||||
|
|
||||||
|
# use the mode to define src and dest's url
|
||||||
|
if self._task.args.get('mode', 'push') == 'pull':
|
||||||
|
# src is a remote path: <user>@<host>, dest is a local path
|
||||||
|
src = self._process_remote(src_host, src, user, task_vars)
|
||||||
|
dest = self._process_origin(dest_host, dest, user, task_vars)
|
||||||
|
else:
|
||||||
|
# src is a local path, dest is a remote path: <user>@<host>
|
||||||
|
src = self._process_origin(src_host, src, user, task_vars)
|
||||||
|
dest = self._process_remote(dest_host, dest, user, task_vars)
|
||||||
|
|
||||||
|
# Allow custom rsync path argument.
|
||||||
|
rsync_path = self._task.args.get('rsync_path', None)
|
||||||
|
|
||||||
|
# If no rsync_path is set, sudo was originally set, and dest is remote then add 'sudo rsync' argument.
|
||||||
|
if not rsync_path and self.transport_overridden and self._connection_info.sudo and not dest_is_local:
|
||||||
|
self._task.args['rsync_path'] = 'sudo rsync'
|
||||||
|
|
||||||
|
# make sure rsync path is quoted.
|
||||||
|
if rsync_path:
|
||||||
|
rsync_path = '"%s"' % rsync_path
|
||||||
|
|
||||||
|
# FIXME: noop stuff still needs to be figured out
|
||||||
|
#module_args = ""
|
||||||
|
#if self.runner.noop_on_check(task_vars):
|
||||||
|
# module_args = "CHECKMODE=True"
|
||||||
|
|
||||||
|
# run the module and store the result
|
||||||
|
result = self.runner._execute_module('synchronize', tmp=tmpmodule_args, complex_args=options, task_vars=task_vars)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
@ -0,0 +1,165 @@
|
|||||||
|
# (c) 2015, 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 base64
|
||||||
|
import os
|
||||||
|
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
from ansible.template import Templar
|
||||||
|
from ansible.utils.hashing import checksum_s
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
|
||||||
|
TRANSFERS_FILES = True
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=dict()):
|
||||||
|
''' handler for template operations '''
|
||||||
|
|
||||||
|
source = self._task.args.get('src', None)
|
||||||
|
dest = self._task.args.get('dest', None)
|
||||||
|
|
||||||
|
if (source is None and 'first_available_file' not in task_vars) or dest is None:
|
||||||
|
return dict(failed=True, msg="src and dest are required")
|
||||||
|
|
||||||
|
if tmp is None:
|
||||||
|
tmp = self._make_tmp_path()
|
||||||
|
|
||||||
|
##################################################################################################
|
||||||
|
# FIXME: this all needs to be sorted out
|
||||||
|
##################################################################################################
|
||||||
|
# if we have first_available_file in our vars
|
||||||
|
# look up the files and use the first one we find as src
|
||||||
|
#if 'first_available_file' in task_vars:
|
||||||
|
# found = False
|
||||||
|
# for fn in self.runner.module_vars.get('first_available_file'):
|
||||||
|
# fn_orig = fn
|
||||||
|
# fnt = template.template(self.runner.basedir, fn, task_vars)
|
||||||
|
# fnd = utils.path_dwim(self.runner.basedir, fnt)
|
||||||
|
# if not os.path.exists(fnd) and '_original_file' in task_vars:
|
||||||
|
# fnd = utils.path_dwim_relative(task_vars['_original_file'], 'templates', fnt, self.runner.basedir, check=False)
|
||||||
|
# if os.path.exists(fnd):
|
||||||
|
# source = fnd
|
||||||
|
# found = True
|
||||||
|
# break
|
||||||
|
# if not found:
|
||||||
|
# result = dict(failed=True, msg="could not find src in first_available_file list")
|
||||||
|
# return ReturnData(conn=conn, comm_ok=False, result=result)
|
||||||
|
#else:
|
||||||
|
# source = template.template(self.runner.basedir, source, task_vars)
|
||||||
|
#
|
||||||
|
# if '_original_file' in task_vars:
|
||||||
|
# source = utils.path_dwim_relative(task_vars['_original_file'], 'templates', source, self.runner.basedir)
|
||||||
|
# else:
|
||||||
|
# source = utils.path_dwim(self.runner.basedir, source)
|
||||||
|
##################################################################################################
|
||||||
|
source = self._loader.path_dwim(source)
|
||||||
|
##################################################################################################
|
||||||
|
|
||||||
|
# Expand any user home dir specification
|
||||||
|
dest = self._remote_expand_user(dest, tmp)
|
||||||
|
|
||||||
|
if dest.endswith("/"): # CCTODO: Fix path for Windows hosts.
|
||||||
|
base = os.path.basename(source)
|
||||||
|
dest = os.path.join(dest, base)
|
||||||
|
|
||||||
|
# template the source data locally & get ready to transfer
|
||||||
|
templar = Templar(basedir=self._loader.get_basedir(), variables=task_vars)
|
||||||
|
try:
|
||||||
|
with open(source, 'r') as f:
|
||||||
|
template_data = f.read()
|
||||||
|
resultant = templar.template(template_data, preserve_trailing_newlines=True)
|
||||||
|
except Exception, e:
|
||||||
|
return dict(failed=True, msg=type(e).__name__ + ": " + str(e))
|
||||||
|
|
||||||
|
local_checksum = checksum_s(resultant)
|
||||||
|
remote_checksum = self._remote_checksum(tmp, dest)
|
||||||
|
|
||||||
|
if remote_checksum in ('0', '2', '3', '4'):
|
||||||
|
# Note: 1 means the file is not present which is fine; template will create it
|
||||||
|
return dict(failed=True, msg="failed to checksum remote file. Checksum error code: %s" % remote_checksum)
|
||||||
|
|
||||||
|
if local_checksum != remote_checksum:
|
||||||
|
# if showing diffs, we need to get the remote value
|
||||||
|
dest_contents = ''
|
||||||
|
|
||||||
|
# FIXME: still need to implement diff mechanism
|
||||||
|
#if self.runner.diff:
|
||||||
|
# # using persist_files to keep the temp directory around to avoid needing to grab another
|
||||||
|
# dest_result = self.runner._execute_module(conn, tmp, 'slurp', "path=%s" % dest, task_vars=task_vars, persist_files=True)
|
||||||
|
# if 'content' in dest_result.result:
|
||||||
|
# dest_contents = dest_result.result['content']
|
||||||
|
# if dest_result.result['encoding'] == 'base64':
|
||||||
|
# dest_contents = base64.b64decode(dest_contents)
|
||||||
|
# else:
|
||||||
|
# raise Exception("unknown encoding, failed: %s" % dest_result.result)
|
||||||
|
|
||||||
|
xfered = self._transfer_data(self._shell.join_path(tmp, 'source'), resultant)
|
||||||
|
|
||||||
|
# fix file permissions when the copy is done as a different user
|
||||||
|
if self._connection_info.sudo and self._connection_info.sudo_user != 'root' or self._connection_info.su and self._connection_info.su_user != 'root':
|
||||||
|
self._remote_chmod('a+r', xfered, tmp)
|
||||||
|
|
||||||
|
# run the copy module
|
||||||
|
new_module_args = self._task.args.copy()
|
||||||
|
new_module_args.update(
|
||||||
|
dict(
|
||||||
|
src=xfered,
|
||||||
|
dest=dest,
|
||||||
|
original_basename=os.path.basename(source),
|
||||||
|
follow=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# FIXME: noop stuff needs to be sorted out
|
||||||
|
#if self.runner.noop_on_check(task_vars):
|
||||||
|
# return ReturnData(conn=conn, comm_ok=True, result=dict(changed=True), diff=dict(before_header=dest, after_header=source, before=dest_contents, after=resultant))
|
||||||
|
#else:
|
||||||
|
# res = self.runner._execute_module(conn, tmp, 'copy', module_args_tmp, task_vars=task_vars, complex_args=complex_args)
|
||||||
|
# if res.result.get('changed', False):
|
||||||
|
# res.diff = dict(before=dest_contents, after=resultant)
|
||||||
|
# return res
|
||||||
|
|
||||||
|
result = self._execute_module(module_name='copy', module_args=new_module_args)
|
||||||
|
if result.get('changed', False):
|
||||||
|
result['diff'] = dict(before=dest_contents, after=resultant)
|
||||||
|
return result
|
||||||
|
|
||||||
|
else:
|
||||||
|
# when running the file module based on the template data, we do
|
||||||
|
# not want the source filename (the name of the template) to be used,
|
||||||
|
# since this would mess up links, so we clear the src param and tell
|
||||||
|
# the module to follow links. When doing that, we have to set
|
||||||
|
# original_basename to the template just in case the dest is
|
||||||
|
# a directory.
|
||||||
|
new_module_args = self._task.args.copy()
|
||||||
|
new_module_args.update(
|
||||||
|
dict(
|
||||||
|
src=None,
|
||||||
|
original_basename=os.path.basename(source),
|
||||||
|
follow=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# FIXME: this may not be required anymore, as the checkmod params
|
||||||
|
# should be in the regular module args?
|
||||||
|
# be sure to task_vars the check mode param into the module args and
|
||||||
|
# rely on the file module to report its changed status
|
||||||
|
#if self.runner.noop_on_check(task_vars):
|
||||||
|
# new_module_args['CHECKMODE'] = True
|
||||||
|
|
||||||
|
return self._execute_module(module_name='file', module_args=new_module_args)
|
||||||
|
|
@ -0,0 +1,118 @@
|
|||||||
|
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||||
|
# (c) 2013, Dylan Martin <dmartin@seattlecentral.edu>
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
## fixes https://github.com/ansible/ansible/issues/3518
|
||||||
|
# http://mypy.pythonblogs.com/12_mypy/archive/1253_workaround_for_python_bug_ascii_codec_cant_encode_character_uxa0_in_position_111_ordinal_not_in_range128.html
|
||||||
|
import sys
|
||||||
|
reload(sys)
|
||||||
|
sys.setdefaultencoding("utf8")
|
||||||
|
import pipes
|
||||||
|
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
|
||||||
|
TRANSFERS_FILES = True
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=dict()):
|
||||||
|
''' handler for unarchive operations '''
|
||||||
|
|
||||||
|
source = self._task.args.get('src', None)
|
||||||
|
dest = self._task.args.get('dest', None)
|
||||||
|
copy = self._task.args.get('copy', True)
|
||||||
|
creates = self._task.args.get('creates', None)
|
||||||
|
|
||||||
|
if source is None or dest is None:
|
||||||
|
return dict(failed=True, msg="src (or content) and dest are required")
|
||||||
|
|
||||||
|
if not tmp:
|
||||||
|
tmp = self._make_tmp_path()
|
||||||
|
|
||||||
|
if creates:
|
||||||
|
# do not run the command if the line contains creates=filename
|
||||||
|
# and the filename already exists. This allows idempotence
|
||||||
|
# of command executions.
|
||||||
|
module_args_tmp = "path=%s" % creates
|
||||||
|
result = self._execute_module(module_name='stat', module_args=dict(path=creates))
|
||||||
|
stat = result.get('stat', None)
|
||||||
|
if stat and stat.get('exists', False):
|
||||||
|
return dict(skipped=True, msg=("skipped, since %s exists" % creates))
|
||||||
|
|
||||||
|
dest = self._remote_expand_user(dest, tmp) # CCTODO: Fix path for Windows hosts.
|
||||||
|
source = os.path.expanduser(source)
|
||||||
|
|
||||||
|
if copy:
|
||||||
|
# FIXME: the original file stuff needs to be reworked
|
||||||
|
if '_original_file' in task_vars:
|
||||||
|
source = self._loader.path_dwim_relative(task_vars['_original_file'], 'files', source)
|
||||||
|
else:
|
||||||
|
source = self._loader.path_dwim(source)
|
||||||
|
|
||||||
|
remote_checksum = self._remote_checksum(tmp, dest)
|
||||||
|
if remote_checksum != '3':
|
||||||
|
return dict(failed=True, msg="dest '%s' must be an existing dir" % dest)
|
||||||
|
elif remote_checksum == '4':
|
||||||
|
return dict(failed=True, msg="python isn't present on the system. Unable to compute checksum")
|
||||||
|
|
||||||
|
if copy:
|
||||||
|
# transfer the file to a remote tmp location
|
||||||
|
tmp_src = tmp + 'source'
|
||||||
|
self._connection.put_file(source, tmp_src)
|
||||||
|
|
||||||
|
# handle diff mode client side
|
||||||
|
# handle check mode client side
|
||||||
|
# fix file permissions when the copy is done as a different user
|
||||||
|
if copy:
|
||||||
|
if self._connection_info.sudo and self._connection_info.sudo_user != 'root' or self._connection_info.su and self._connection_info.su_user != 'root':
|
||||||
|
# FIXME: noop stuff needs to be reworked
|
||||||
|
#if not self.runner.noop_on_check(task_vars):
|
||||||
|
# self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp)
|
||||||
|
self._remote_chmod(tmp, 'a+r', tmp_src)
|
||||||
|
|
||||||
|
# Build temporary module_args.
|
||||||
|
new_module_args = self._task.args.copy()
|
||||||
|
new_module_args.update(
|
||||||
|
dict(
|
||||||
|
src=tmp_src,
|
||||||
|
original_basename=os.path.basename(source),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# make sure checkmod is passed on correctly
|
||||||
|
# FIXME: noop again, probably doesn't need to be done here anymore?
|
||||||
|
#if self.runner.noop_on_check(task_vars):
|
||||||
|
# new_module_args['CHECKMODE'] = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
new_module_args = self._task.args.copy()
|
||||||
|
new_module_args.update(
|
||||||
|
dict(
|
||||||
|
original_basename=os.path.basename(source),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# make sure checkmod is passed on correctly
|
||||||
|
# FIXME: noop again, probably doesn't need to be done here anymore?
|
||||||
|
#if self.runner.noop_on_check(task_vars):
|
||||||
|
# module_args += " CHECKMODE=True"
|
||||||
|
|
||||||
|
# execute the unarchive module now, with the updated args
|
||||||
|
return self._execute_module(module_args=new_module_args)
|
||||||
|
|
Loading…
Reference in New Issue