community.general/lib/ansible/runner/action_plugins/script.py
Dan Rue 67eb9f301d Do not mark "skipped" when changed is false
When using the "creates" option with the script module, set changed
to False if the file already exists. This behavior is consistent with
other modules which use "creates", such as command and shell.
2015-02-17 15:14:21 -06:00

137 lines
5.6 KiB
Python

# (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 re
import shlex
import ansible.constants as C
from ansible.utils import template
from ansible import utils
from ansible import errors
from ansible.runner.return_data import ReturnData
class ActionModule(object):
TRANSFERS_FILES = True
def __init__(self, runner):
self.runner = runner
def run(self, conn, tmp, module_name, module_args, inject, complex_args=None, **kwargs):
''' handler for file transfer operations '''
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'))
# extract ansible reserved parameters
# From library/command keep in sync
creates = None
removes = None
r = re.compile(r'(^|\s)(creates|removes)=(?P<quote>[\'"])?(.*?)(?(quote)(?<!\\)(?P=quote))((?<!\\)(?=\s)|$)')
for m in r.finditer(module_args):
v = m.group(4).replace("\\", "")
if m.group(2) == "creates":
creates = v
elif m.group(2) == "removes":
removes = v
module_args = r.sub("", module_args)
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
module_return = self.runner._execute_module(conn, tmp, 'stat', module_args_tmp, inject=inject,
complex_args=complex_args, persist_files=True)
stat = module_return.result.get('stat', None)
if stat and stat.get('exists', False):
return ReturnData(
conn=conn,
comm_ok=True,
result=dict(
changed=False,
msg=("skipped, since %s exists" % creates)
)
)
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.
module_args_tmp = "path=%s" % removes
module_return = self.runner._execute_module(conn, tmp, 'stat', module_args_tmp, inject=inject,
complex_args=complex_args, persist_files=True)
stat = module_return.result.get('stat', None)
if stat and not stat.get('exists', False):
return ReturnData(
conn=conn,
comm_ok=True,
result=dict(
changed=False,
msg=("skipped, since %s does not exist" % removes)
)
)
# Decode the result of shlex.split() to UTF8 to get around a bug in that's been fixed in Python 2.7 but not Python 2.6.
# See: http://bugs.python.org/issue6988
tokens = shlex.split(module_args.encode('utf8'))
tokens = [s.decode('utf8') for s in tokens]
# extract source script
source = tokens[0]
# FIXME: error handling
args = " ".join(tokens[1:])
source = template.template(self.runner.basedir, source, inject)
if '_original_file' in inject:
source = utils.path_dwim_relative(inject['_original_file'], 'files', source, self.runner.basedir)
else:
source = utils.path_dwim(self.runner.basedir, source)
# transfer the file to a remote tmp location
source = source.replace('\x00', '') # why does this happen here?
args = args.replace('\x00', '') # why does this happen here?
tmp_src = conn.shell.join_path(tmp, os.path.basename(source))
tmp_src = tmp_src.replace('\x00', '')
conn.put_file(source, tmp_src)
sudoable = True
# set file permissions, more permissive when the copy is done as a different user
if ((self.runner.sudo and self.runner.sudo_user != 'root') or
(self.runner.su and self.runner.su_user != 'root')):
chmod_mode = 'a+rx'
sudoable = False
else:
chmod_mode = '+rx'
self.runner._remote_chmod(conn, chmod_mode, tmp_src, tmp, sudoable=sudoable, su=self.runner.su)
# add preparation steps to one ssh roundtrip executing the script
env_string = self.runner._compute_environment_string(conn, inject)
module_args = ' '.join([env_string, tmp_src, args])
handler = utils.plugins.action_loader.get('raw', self.runner)
result = handler.run(conn, tmp, 'raw', module_args, inject)
# clean up after
if "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES:
self.runner._remove_tmp_path(conn, tmp)
result.result['changed'] = True
return result