2012-02-23 23:19:06 +01:00
#!/usr/bin/python
2012-08-03 03:20:43 +02:00
# -*- coding: utf-8 -*-
2012-02-23 23:19:06 +01:00
2012-02-29 01:08:09 +01:00
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>, and others
#
# 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/>.
2012-02-24 10:35:51 +01:00
2014-10-08 13:19:26 +02:00
import copy
2012-02-23 23:19:06 +01:00
import sys
import datetime
2014-12-14 23:53:21 +01:00
import glob
2012-02-24 10:35:51 +01:00
import traceback
2012-10-01 08:30:53 +02:00
import re
2012-03-14 05:39:18 +01:00
import shlex
import os
2012-07-25 00:52:52 +02:00
2012-09-28 21:55:49 +02:00
DOCUMENTATION = '''
- - -
module : command
short_description : Executes a command on a remote node
2015-07-30 23:04:41 +02:00
version_added : historical
2012-09-28 21:55:49 +02:00
description :
2012-11-21 18:49:30 +01:00
- The M ( command ) module takes the command name followed by a list of space - delimited arguments .
2012-09-28 21:55:49 +02:00
- The given command will be executed on all selected nodes . It will not be
processed through the shell , so variables like C ( $ HOME ) and operations
2014-01-27 21:56:52 +01:00
like C ( " < " ) , C ( " > " ) , C ( " | " ) , and C ( " & " ) will not work ( use the M ( shell )
module if you need these features ) .
2012-09-28 21:55:49 +02:00
options :
free_form :
description :
2014-04-03 23:34:11 +02:00
- the command module takes a free form command to run . There is no parameter actually named ' free form ' .
See the examples !
2012-09-28 21:55:49 +02:00
required : true
default : null
creates :
description :
2015-12-12 23:16:47 +01:00
- a filename or ( since 2.0 ) glob pattern , when it already exists , this step will B ( not ) be run .
2012-09-28 21:55:49 +02:00
required : no
default : null
2012-10-03 04:32:17 +02:00
removes :
description :
2015-12-12 23:16:47 +01:00
- a filename or ( since 2.0 ) glob pattern , when it does not exist , this step will B ( not ) be run .
2012-10-03 04:32:17 +02:00
version_added : " 0.8 "
required : no
default : null
2012-09-28 21:55:49 +02:00
chdir :
description :
- cd into this directory before running the command
2013-11-28 03:23:03 +01:00
version_added : " 0.6 "
2012-09-28 21:55:49 +02:00
required : false
default : null
2012-11-08 14:56:16 +01:00
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 "
2013-12-02 10:54:15 +01:00
warn :
2014-08-22 21:33:57 +02:00
version_added : " 1.8 "
default : yes
2013-12-02 10:54:15 +01:00
description :
2014-08-22 22:12:32 +02:00
- if command warnings are on in ansible . cfg , do not warn about this particular line if set to no / false .
2013-12-02 10:54:15 +01:00
required : false
2012-09-28 21:55:49 +02:00
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
M ( command ) module is much more secure as it ' s not affected by the user ' s
environment .
2013-06-14 11:53:43 +02:00
- " 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. "
2015-06-15 21:53:30 +02:00
author :
- Ansible Core Team
- Michael DeHaan
2012-09-28 21:55:49 +02:00
'''
2013-06-14 11:53:43 +02:00
EXAMPLES = '''
2014-06-24 13:45:49 +02:00
# Example from Ansible Playbooks.
2013-06-14 11:53:43 +02:00
- command : / sbin / shutdown - t now
2014-06-24 13:45:49 +02:00
# Run the command if the specified file does not exist.
2013-06-14 11:53:43 +02:00
- command : / usr / bin / make_database . sh arg1 arg2 creates = / path / to / database
2014-06-24 13:45:49 +02:00
# You can also use the 'args' form to provide the options. This command
# will change the working directory to somedir/ and will only run when
# /path/to/database doesn't exist.
- command : / usr / bin / make_database . sh arg1 arg2
args :
chdir : somedir /
creates : / path / to / database
2013-06-14 11:53:43 +02:00
'''
2014-10-08 13:25:02 +02:00
# Dict of options and their defaults
2014-10-08 13:19:26 +02:00
OPTIONS = { ' chdir ' : None ,
' creates ' : None ,
' executable ' : None ,
' NO_LOG ' : None ,
' removes ' : None ,
' warn ' : True ,
}
2014-07-22 03:40:58 +02:00
# This is a pretty complex regex, which functions as follows:
#
# 1. (^|\s)
# ^ look for a space or the beginning of the line
2014-10-08 13:19:26 +02:00
# 2. ({options_list})=
# ^ expanded to (chdir|creates|executable...)=
# look for a valid param, followed by an '='
2014-07-22 03:40:58 +02:00
# 3. (?P<quote>[\'"])?
# ^ look for an optional quote character, which can either be
# a single or double quote character, and store it for later
# 4. (.*?)
# ^ match everything in a non-greedy manner until...
# 5. (?(quote)(?<!\\)(?P=quote))((?<!\\)(?=\s)|$)
# ^ a non-escaped space or a non-escaped quote of the same kind
# that was matched in the first 'quote' is found, or the end of
# the line is reached
2014-10-08 13:19:26 +02:00
OPTIONS_REGEX = ' | ' . join ( OPTIONS . keys ( ) )
PARAM_REGEX = re . compile (
2015-01-27 18:42:38 +01:00
r ' (^| \ s)( ' + OPTIONS_REGEX +
r ' )=(?P<quote>[ \' " ])?(.*?)(?(quote)(?<! \\ )(?P=quote))((?<! \\ )(?= \ s)|$) '
2014-10-08 13:19:26 +02:00
)
2013-12-02 10:54:15 +01:00
def check_command ( commandline ) :
arguments = { ' chown ' : ' owner ' , ' chmod ' : ' mode ' , ' chgrp ' : ' group ' ,
' ln ' : ' state=link ' , ' mkdir ' : ' state=directory ' ,
' rmdir ' : ' state=absent ' , ' rm ' : ' state=absent ' , ' touch ' : ' state=touch ' }
commands = { ' git ' : ' git ' , ' hg ' : ' hg ' , ' curl ' : ' get_url ' , ' wget ' : ' get_url ' ,
' svn ' : ' subversion ' , ' service ' : ' service ' ,
2015-12-22 10:07:08 +01:00
' mount ' : ' mount ' , ' rpm ' : ' yum, dnf or zypper ' , ' yum ' : ' yum ' , ' apt-get ' : ' apt-get ' ,
2013-12-02 10:54:15 +01:00
' tar ' : ' unarchive ' , ' unzip ' : ' unarchive ' , ' sed ' : ' template or lineinfile ' ,
2015-12-22 10:07:08 +01:00
' rsync ' : ' synchronize ' , ' dnf ' : ' dnf ' , ' zypper ' : ' zypper ' }
2015-07-27 17:02:24 +02:00
become = [ ' sudo ' , ' su ' , ' pbrun ' , ' pfexec ' , ' runas ' ]
2013-12-02 10:54:15 +01:00
warnings = list ( )
command = os . path . basename ( commandline . split ( ) [ 0 ] )
if command in arguments :
warnings . append ( " Consider using file module with %s rather than running %s " % ( arguments [ command ] , command ) )
if command in commands :
warnings . append ( " Consider using %s module rather than running %s " % ( commands [ command ] , command ) )
2015-07-27 17:02:24 +02:00
if command in become :
warnings . append ( " Consider using ' become ' , ' become_method ' , and ' become_user ' rather than running %s " % ( command , ) )
2013-12-02 10:54:15 +01:00
return warnings
2014-07-22 03:40:58 +02:00
2012-07-25 00:52:52 +02:00
def main ( ) :
# the command module is the one ansible module that does not take key=value args
# hence don't copy this one if you are looking to build others!
2014-11-19 22:54:47 +01:00
module = AnsibleModule (
argument_spec = dict (
_raw_params = dict ( ) ,
_uses_shell = dict ( type = ' bool ' , default = False ) ,
chdir = dict ( ) ,
executable = dict ( ) ,
creates = dict ( ) ,
removes = dict ( ) ,
warn = dict ( type = ' bool ' , default = True ) ,
)
)
2012-07-25 00:52:52 +02:00
2014-11-19 22:54:47 +01:00
shell = module . params [ ' _uses_shell ' ]
2012-07-30 17:39:45 +02:00
chdir = module . params [ ' chdir ' ]
2012-11-08 14:56:16 +01:00
executable = module . params [ ' executable ' ]
2014-11-19 22:54:47 +01:00
args = module . params [ ' _raw_params ' ]
2013-02-28 23:38:52 +01:00
creates = module . params [ ' creates ' ]
removes = module . params [ ' removes ' ]
2014-10-08 13:30:20 +02:00
warn = module . params [ ' warn ' ]
2012-07-25 00:52:52 +02:00
2012-08-17 03:40:52 +02:00
if args . strip ( ) == ' ' :
2012-10-30 10:36:11 +01:00
module . fail_json ( rc = 256 , msg = " no command given " )
2012-08-14 02:17:07 +02:00
2012-07-30 17:39:45 +02:00
if chdir :
2015-01-14 15:02:49 +01:00
chdir = os . path . abspath ( os . path . expanduser ( chdir ) )
2013-05-29 17:05:11 +02:00
os . chdir ( chdir )
2012-07-30 17:39:45 +02:00
2013-02-28 23:38:52 +01:00
if creates :
# do not run the command if the line contains creates=filename
# and the filename already exists. This allows idempotence
# of command executions.
v = os . path . expanduser ( creates )
2014-12-14 23:53:21 +01:00
if glob . glob ( v ) :
2013-02-28 23:38:52 +01:00
module . exit_json (
cmd = args ,
stdout = " skipped, since %s exists " % v ,
changed = False ,
stderr = False ,
rc = 0
)
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.
v = os . path . expanduser ( removes )
2014-12-14 23:53:21 +01:00
if not glob . glob ( v ) :
2013-02-28 23:38:52 +01:00
module . exit_json (
cmd = args ,
stdout = " skipped, since %s does not exist " % v ,
changed = False ,
stderr = False ,
rc = 0
)
2013-12-02 10:54:15 +01:00
warnings = list ( )
if warn :
warnings = check_command ( args )
2012-07-25 00:52:52 +02:00
if not shell :
args = shlex . split ( args )
startd = datetime . datetime . now ( )
2014-03-10 22:11:24 +01:00
rc , out , err = module . run_command ( args , executable = executable , use_unsafe_shell = shell )
2012-07-25 00:52:52 +02:00
endd = datetime . datetime . now ( )
delta = endd - startd
if out is None :
2012-08-11 18:35:58 +02:00
out = ' '
2012-07-25 00:52:52 +02:00
if err is None :
2012-08-11 18:35:58 +02:00
err = ' '
2012-07-25 00:52:52 +02:00
module . exit_json (
2013-12-02 10:54:15 +01:00
cmd = args ,
stdout = out . rstrip ( " \r \n " ) ,
stderr = err . rstrip ( " \r \n " ) ,
rc = rc ,
start = str ( startd ) ,
end = str ( endd ) ,
delta = str ( delta ) ,
changed = True ,
warnings = warnings
2012-07-25 00:52:52 +02:00
)
2013-12-02 21:11:23 +01:00
# import module snippets
from ansible . module_utils . basic import *
2014-07-29 21:16:47 +02:00
from ansible . module_utils . splitter import *
2012-07-25 00:52:52 +02:00
main ( )