From 75e3b59bbdc2bce1e7cfc9d98f2e18dd8aa1bd3f Mon Sep 17 00:00:00 2001
From: James Cammarata <jcammarata@ansibleworks.com>
Date: Fri, 31 Jan 2014 16:09:10 -0600
Subject: [PATCH] Adding no_log: capability for tasks

Fixes #4088
---
 CHANGELOG.md                                |  1 +
 lib/ansible/module_utils/basic.py           | 13 ++++++++++---
 lib/ansible/playbook/__init__.py            |  3 ++-
 lib/ansible/playbook/play.py                |  2 +-
 lib/ansible/playbook/task.py                |  8 ++++----
 lib/ansible/runner/__init__.py              |  4 ++++
 lib/ansible/runner/action_plugins/copy.py   |  2 ++
 lib/ansible/runner/action_plugins/normal.py |  3 +++
 library/commands/command                    |  4 +++-
 9 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 65e3c020e07..31f4ac12c35 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ Major features/changes:
 * localhost/127.0.0.1 is not required to be in inventory if referenced, if not in inventory, it does not implicitly appear in the 'all' group.
 * git module has new parameters (accept_hostkey, key_file, ssh_opts) to ease the usage of git and ssh protocols. 
 * when using accelerate mode, the daemon will now be restarted when specifying a different remote_user between plays.
+* added no_log: option for tasks. When used, no logging information will be sent to syslog during the module execution.
 
 
 New modules:
diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
index 29766efb3c3..0cfe5bbf4ee 100644
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -175,6 +175,7 @@ class AnsibleModule(object):
         self.argument_spec = argument_spec
         self.supports_check_mode = supports_check_mode
         self.check_mode = False
+        self.no_log = no_log
         
         self.aliases = {}
         
@@ -186,13 +187,14 @@ class AnsibleModule(object):
         os.environ['LANG'] = MODULE_LANG
         (self.params, self.args) = self._load_params()
 
-        self._legal_inputs = [ 'CHECKMODE' ]
+        self._legal_inputs = [ 'CHECKMODE', 'NO_LOG' ]
         
         self.aliases = self._handle_aliases()
 
         if check_invalid_arguments:
             self._check_invalid_arguments()
         self._check_for_check_mode()
+        self._check_for_no_log()
 
         self._set_defaults(pre=True)
 
@@ -205,7 +207,7 @@ class AnsibleModule(object):
             self._check_required_one_of(required_one_of)
 
         self._set_defaults(pre=False)
-        if not no_log:
+        if not self.no_log:
             self._log_invocation()
 
     def load_file_common_arguments(self, params):
@@ -558,9 +560,14 @@ class AnsibleModule(object):
                 if self.supports_check_mode:
                     self.check_mode = True
 
+    def _check_for_no_log(self):
+        for (k,v) in self.params.iteritems():
+            if k == 'NO_LOG':
+                self.no_log = self.boolean(v)
+
     def _check_invalid_arguments(self):
         for (k,v) in self.params.iteritems():
-            if k == 'CHECKMODE':
+            if k in ('CHECKMODE', 'NO_LOG'):
                 continue
             if k not in self._legal_inputs:
                 self.fail_json(msg="unsupported parameter for module: %s" % k)
diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py
index 61db0512fe5..0d725485826 100644
--- a/lib/ansible/playbook/__init__.py
+++ b/lib/ansible/playbook/__init__.py
@@ -343,7 +343,8 @@ class PlayBook(object):
             su=task.su,
             su_user=task.su_user,
             su_pass=task.su_pass,
-            run_hosts=hosts
+            run_hosts=hosts,
+            no_log=task.no_log,
         )
 
         if task.async_seconds == 0:
diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py
index a2d5c8880d6..94f4dc074ae 100644
--- a/lib/ansible/playbook/play.py
+++ b/lib/ansible/playbook/play.py
@@ -504,7 +504,7 @@ class Play(object):
                         elif type(x[k]) is list:
                             for i in x[k]:
                                 included_additional_conditions.insert(0, i)
-                    elif k in ("include", "vars", "default_vars", "sudo", "sudo_user", "role_name"):
+                    elif k in ("include", "vars", "default_vars", "sudo", "sudo_user", "role_name", "no_log"):
                         continue
                     else:
                         include_vars[k] = x[k]
diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py
index 8b358486b98..99e99d4ba18 100644
--- a/lib/ansible/playbook/task.py
+++ b/lib/ansible/playbook/task.py
@@ -31,7 +31,7 @@ class Task(object):
         'local_action', 'transport', 'sudo', 'remote_user', 'sudo_user', 'sudo_pass',
         'items_lookup_plugin', 'items_lookup_terms', 'environment', 'args',
         'any_errors_fatal', 'changed_when', 'failed_when', 'always_run', 'delay', 'retries', 'until',
-        'su', 'su_user', 'su_pass'
+        'su', 'su_user', 'su_pass', 'no_log',
     ]
 
     # to prevent typos and such
@@ -41,7 +41,7 @@ class Task(object):
          'delegate_to', 'local_action', 'transport', 'remote_user', 'sudo', 'sudo_user',
          'sudo_pass', 'when', 'connection', 'environment', 'args',
          'any_errors_fatal', 'changed_when', 'failed_when', 'always_run', 'delay', 'retries', 'until',
-         'su', 'su_user', 'su_pass'
+         'su', 'su_user', 'su_pass', 'no_log',
     ]
 
     def __init__(self, play, ds, module_vars=None, default_vars=None, additional_conditions=None, role_name=None):
@@ -106,7 +106,6 @@ class Task(object):
                 when_name = x.replace("when_","")
                 ds['when'] = "%s %s" % (when_name, ds[x])
                 ds.pop(x)
-
             elif not x in Task.VALID_KEYS:
                 raise errors.AnsibleError("%s is not a legal parameter in an Ansible task or handler" % x)
 
@@ -122,7 +121,8 @@ class Task(object):
         self.su           = utils.boolean(ds.get('su', play.su))
         self.environment  = ds.get('environment', {})
         self.role_name    = role_name
-        
+        self.no_log       = utils.boolean(ds.get('no_log', "false"))
+
         #Code to allow do until feature in a Task 
         if 'until' in ds:
             if not ds.get('register'):
diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py
index 4a06fcee524..c48548b0e0f 100644
--- a/lib/ansible/runner/__init__.py
+++ b/lib/ansible/runner/__init__.py
@@ -145,6 +145,7 @@ class Runner(object):
         su_user=None,                       # User to su to when running command, ex: 'root'
         su_pass=C.DEFAULT_SU_PASS,
         run_hosts=None,                     # an optional list of pre-calculated hosts to run on
+        no_log=False,                       # option to enable/disable logging for a given task
         ):
 
         # used to lock multiprocess inputs and outputs at various levels
@@ -196,6 +197,7 @@ class Runner(object):
         self.su_user_var      = su_user
         self.su_user          = None
         self.su_pass          = su_pass
+        self.no_log           = no_log
 
         if self.transport == 'smart':
             # if the transport is 'smart' see if SSH can support ControlPersist if not use paramiko
@@ -341,6 +343,8 @@ class Runner(object):
                 # if module isn't using AnsibleModuleCommon infrastructure we can't be certain it knows how to
                 # do --check mode, so to be safe we will not run it.
                 return ReturnData(conn=conn, result=dict(skipped=True, msg="cannot yet run check mode against old-style modules"))
+            elif 'NO_LOG' in args:
+                return ReturnData(conn=conn, result=dict(skipped=True, msg="cannot use no_log: with old-style modules"))
 
             args = template.template(self.basedir, args, inject)
 
diff --git a/lib/ansible/runner/action_plugins/copy.py b/lib/ansible/runner/action_plugins/copy.py
index 6cff391d502..e1dce790b98 100644
--- a/lib/ansible/runner/action_plugins/copy.py
+++ b/lib/ansible/runner/action_plugins/copy.py
@@ -220,6 +220,8 @@ class ActionModule(object):
                                   pipes.quote(tmp_src), pipes.quote(source_rel))
                 if self.runner.noop_on_check(inject):
                     module_args_tmp = "%s CHECKMODE=True" % module_args_tmp
+                if self.runner.no_log:
+                    module_args_tmp = "%s NO_LOG=True" % module_args_tmp
                 module_return = self.runner._execute_module(conn, tmp, 'file', module_args_tmp, inject=inject, complex_args=complex_args)
 
             module_result = module_return.result
diff --git a/lib/ansible/runner/action_plugins/normal.py b/lib/ansible/runner/action_plugins/normal.py
index 1df620c410a..8500c6641c3 100644
--- a/lib/ansible/runner/action_plugins/normal.py
+++ b/lib/ansible/runner/action_plugins/normal.py
@@ -45,6 +45,9 @@ class ActionModule(object):
             # python modules for now
             module_args += " CHECKMODE=True"
 
+        if self.runner.no_log:
+            module_args += " NO_LOG=True"
+
         # shell and command are the same module
         if module_name == 'shell':
             module_name = 'command'
diff --git a/library/commands/command b/library/commands/command
index 4365daec04d..76d2f828d0c 100644
--- a/library/commands/command
+++ b/library/commands/command
@@ -184,7 +184,7 @@ class CommandModule(AnsibleModule):
             args = args.replace("#USE_SHELL", "")
             params['shell'] = True
 
-        r = re.compile(r'(^|\s)(creates|removes|chdir|executable)=(?P<quote>[\'"])?(.*?)(?(quote)(?<!\\)(?P=quote))((?<!\\)(?=\s)|$)')
+        r = re.compile(r'(^|\s)(creates|removes|chdir|executable|NO_LOG)=(?P<quote>[\'"])?(.*?)(?(quote)(?<!\\)(?P=quote))((?<!\\)(?=\s)|$)')
         for m in r.finditer(args):
             v = m.group(4).replace("\\", "")
             if m.group(2) == "creates":
@@ -203,6 +203,8 @@ class CommandModule(AnsibleModule):
                 if not (os.path.exists(v)):
                     self.fail_json(rc=258, msg="cannot use executable '%s': file does not exist" % v)
                 params['executable'] = v
+            elif m.group(2) == "NO_LOG":
+                params['NO_LOG'] = self.boolean(v)
         args = r.sub("", args)
         params['args'] = args
         return (params, params['args'])