From 7bced14e8c76e60ff956eb092b83dd56faa49970 Mon Sep 17 00:00:00 2001 From: David Shrewsbury Date: Thu, 19 Apr 2018 11:05:38 -0400 Subject: [PATCH] Update to Ansible 2.5 Our custom command.py Ansible module is updated to match the version from 2.5, plus our additions. strip_internal_keys() is moved within Ansible yet again. Change-Id: Iab951c11b23a24757cf5334b36bc8f7d12e19db0 Depends-On: https://review.openstack.org/567007 --- requirements.txt | 2 +- zuul/ansible/callback/zuul_json.py | 8 +- zuul/ansible/callback/zuul_stream.py | 2 +- zuul/ansible/library/command.py | 137 +++++++++++++++------------ 4 files changed, 84 insertions(+), 65 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7d01b9146e..c8406e02d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ gear>=0.9.0,<1.0.0 apscheduler>=3.0 PrettyTable>=0.6,<0.8 babel>=1.0 -ansible>=2.4.1,<2.5 +ansible>=2.5.1,<2.6 netaddr kazoo sqlalchemy diff --git a/zuul/ansible/callback/zuul_json.py b/zuul/ansible/callback/zuul_json.py index d20b2b0be4..951b300639 100644 --- a/zuul/ansible/callback/zuul_json.py +++ b/zuul/ansible/callback/zuul_json.py @@ -31,8 +31,12 @@ try: # It's here in 2.3 from ansible.vars import strip_internal_keys except ImportError: - # It's here in 2.4 - from ansible.vars.manager import strip_internal_keys + try: + # It's here in 2.4 + from ansible.vars.manager import strip_internal_keys + except ImportError: + # It's here in 2.5 + from ansible.vars.clean import strip_internal_keys from zuul.ansible import logconfig diff --git a/zuul/ansible/callback/zuul_stream.py b/zuul/ansible/callback/zuul_stream.py index 15b491cf1d..f733f9fffe 100644 --- a/zuul/ansible/callback/zuul_stream.py +++ b/zuul/ansible/callback/zuul_stream.py @@ -194,7 +194,7 @@ class CallbackModule(default.CallbackModule): if self._play.strategy != 'free': task_name = self._print_task_banner(task) - if task.action == 'command': + if task.action in ('command', 'shell'): log_id = uuid.uuid4().hex task.args['zuul_log_id'] = log_id play_vars = self._play._variable_manager._hostvars diff --git a/zuul/ansible/library/command.py b/zuul/ansible/library/command.py index 2190d59d00..3793881902 100755 --- a/zuul/ansible/library/command.py +++ b/zuul/ansible/library/command.py @@ -1,28 +1,20 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright (c) 2016 Red Hat, Inc. -# Copyright (c) 2016 IBM Corp. -# (c) 2012, Michael DeHaan , and others -# (c) 2016, Toshio Kuratomi +# Copyright: (c) 2012, Michael DeHaan , and others +# Copyright: (c) 2016, Toshio Kuratomi # -# This module 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. -# -# This software 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 this software. If not, see . +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['stableinterface'], 'supported_by': 'core'} + # flake8: noqa # This file shares a significant chunk of code with an upstream ansible # function, run_command. The goal is to not have to fork quite so much @@ -43,42 +35,30 @@ description: processed through the shell, so variables like C($HOME) and operations like C("<"), C(">"), C("|"), C(";") and C("&") will not work (use the M(shell) module if you need these features). + - For Windows targets, use the M(win_command) module instead. options: free_form: description: - - the command module takes a free form command to run. There is no parameter actually named 'free form'. + - The command module takes a free form command to run. There is no parameter actually named 'free form'. See the examples! - required: true - default: null + required: yes creates: description: - - a filename or (since 2.0) glob pattern, when it already exists, this step will B(not) be run. - required: no - default: null + - A filename or (since 2.0) glob pattern, when it already exists, this step will B(not) be run. removes: description: - - a filename or (since 2.0) glob pattern, when it does not exist, this step will B(not) be run. + - A filename or (since 2.0) glob pattern, when it does not exist, this step will B(not) be run. version_added: "0.8" - required: no - default: null chdir: description: - - cd into this directory before running the command + - Change into this directory before running the command. version_added: "0.6" - required: false - default: null - executable: - description: - - change the shell used to execute the command. Should be an absolute path to the executable. - required: false - default: null - version_added: "0.9" warn: - version_added: "1.8" - default: yes description: - - if command warnings are on in ansible.cfg, do not warn about this particular line if set to no/false. - required: false + - If command_warnings are on in ansible.cfg, do not warn about this particular line if set to C(no). + type: bool + default: 'yes' + version_added: "1.8" stdin: version_added: "2.4" description: @@ -87,9 +67,12 @@ options: default: null notes: - If you want to run a command through the shell (say you are using C(<), C(>), C(|), etc), you actually want the M(shell) module instead. - The C(command) module is much more secure as it's not affected by the user's environment. + Parsing shell metacharacters can lead to unexpected commands being executed if quoting is not done correctly so it is more secure to + use the C(command) module when possible. - " C(creates), C(removes), and C(chdir) can be specified after the command. For instance, if you only want to run a command if a certain file does not exist, use this." + - The C(executable) parameter is removed since version 2.4. If you have a need for this parameter, use the M(shell) module instead. + - For Windows targets, use the M(win_command) module instead. author: - Ansible Core Team - Michael DeHaan @@ -115,33 +98,56 @@ EXAMPLES = ''' register: myoutput ''' +RETURN = ''' +cmd: + description: the cmd that was run on the remote machine + returned: always + type: list + sample: + - echo + - hello +delta: + description: cmd end time - cmd start time + returned: always + type: string + sample: 0:00:00.001529 +end: + description: cmd end time + returned: always + type: string + sample: '2017-09-29 22:03:48.084657' +start: + description: cmd start time + returned: always + type: string + sample: '2017-09-29 22:03:48.083128' +''' + import datetime import glob -import pipes -import re -import shlex import os +import shlex -import getpass -import select +from ansible.module_utils.basic import AnsibleModule + +# Imports needed for Zuul things +import re import subprocess import traceback import threading - -from ansible.module_utils.basic import AnsibleModule, heuristic_log_sanitize +from ansible.module_utils.basic import heuristic_log_sanitize from ansible.module_utils.six import ( PY2, PY3, b, binary_type, - integer_types, - iteritems, string_types, text_type, ) -from ansible.module_utils.six.moves import map, reduce, shlex_quote +from ansible.module_utils.six.moves import shlex_quote from ansible.module_utils._text import to_native, to_bytes, to_text + LOG_STREAM_FILE = '/tmp/console-{log_uuid}.log' PASSWD_ARG_RE = re.compile(r'^[-]{0,2}pass[-]?(word|wd)?') # List to save stdout log lines in as we collect them @@ -518,17 +524,30 @@ def check_command(module, commandline): arguments = {'chown': 'owner', 'chmod': 'mode', 'chgrp': 'group', 'ln': 'state=link', 'mkdir': 'state=directory', 'rmdir': 'state=absent', 'rm': 'state=absent', 'touch': 'state=touch'} - commands = {'hg': 'hg', 'curl': 'get_url or uri', 'wget': 'get_url or uri', + commands = {'curl': 'get_url or uri', 'wget': 'get_url or uri', 'svn': 'subversion', 'service': 'service', 'mount': 'mount', 'rpm': 'yum, dnf or zypper', 'yum': 'yum', 'apt-get': 'apt', - 'tar': 'unarchive', 'unzip': 'unarchive', 'sed': 'template or lineinfile', + 'tar': 'unarchive', 'unzip': 'unarchive', 'sed': 'replace, lineinfile or template', 'dnf': 'dnf', 'zypper': 'zypper'} become = ['sudo', 'su', 'pbrun', 'pfexec', 'runas', 'pmrun'] command = os.path.basename(commandline.split()[0]) + + disable_suffix = "If you need to use command because {mod} is insufficient you can add" \ + " warn=False to this command task or set command_warnings=False in" \ + " ansible.cfg to get rid of this message." + substitutions = {'mod': None, 'cmd': command} + if command in arguments: - module.warn("Consider using file module with %s rather than running %s" % (arguments[command], command)) + msg = "Consider using the {mod} module with {subcmd} rather than running {cmd}. " + disable_suffix + substitutions['mod'] = 'file' + substitutions['subcmd'] = arguments[command] + module.warn(msg.format(**substitutions)) + if command in commands: - module.warn("Consider using %s module rather than running %s" % (commands[command], command)) + msg = "Consider using the {mod} module rather than running {cmd}. " + disable_suffix + substitutions['mod'] = commands[command] + module.warn(msg.format(**substitutions)) + if command in become: module.warn("Consider using 'become', 'become_method', and 'become_user' rather than running %s" % (command,)) @@ -545,9 +564,10 @@ def main(): executable=dict(), creates=dict(type='path'), removes=dict(type='path'), + # The default for this really comes from the action plugin warn=dict(type='bool', default=True), stdin=dict(required=False), - zuul_log_id = dict(type='str'), + zuul_log_id=dict(type='str'), ) ) @@ -565,7 +585,7 @@ def main(): module.warn("As of Ansible 2.4, the parameter 'executable' is no longer supported with the 'command' module. Not using '%s'." % executable) executable = None - if args.strip() == '': + if not args or args.strip() == '': module.fail_json(rc=256, msg="no command given") if chdir: @@ -608,11 +628,6 @@ def main(): endd = datetime.datetime.now() delta = endd - startd - if out is None: - out = b('') - if err is None: - err = b('') - result = dict( cmd=args, stdout=out.rstrip(b"\r\n"), @@ -632,4 +647,4 @@ def main(): if __name__ == '__main__': - main() \ No newline at end of file + main()