1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Force command action to not be executed by the shell unless specifically enabled

This commit is contained in:
James Tanner 2014-03-10 16:11:24 -05:00 committed by James Cammarata
parent 5bc6eafba5
commit a02641c020
19 changed files with 427 additions and 245 deletions

View file

@ -990,12 +990,13 @@ class AnsibleModule(object):
# rename might not preserve context # rename might not preserve context
self.set_context_if_different(dest, context, False) self.set_context_if_different(dest, context, False)
def run_command(self, args, check_rc=False, close_fds=False, executable=None, data=None, binary_data=False, path_prefix=None): def run_command(self, args, check_rc=False, close_fds=False, executable=None, data=None, binary_data=False, path_prefix=None, cwd=None, use_unsafe_shell=False):
''' '''
Execute a command, returns rc, stdout, and stderr. Execute a command, returns rc, stdout, and stderr.
args is the command to run args is the command to run
If args is a list, the command will be run with shell=False. If args is a list, the command will be run with shell=False.
Otherwise, the command will be run with shell=True when args is a string. If args is a string and use_unsafe_shell=False it will split args to a list and run with shell=False
If args is a string and use_unsafe_shell=True it run with shell=True.
Other arguments: Other arguments:
- check_rc (boolean) Whether to call fail_json in case of - check_rc (boolean) Whether to call fail_json in case of
non zero RC. Default is False. non zero RC. Default is False.
@ -1004,13 +1005,18 @@ class AnsibleModule(object):
- executable (string) See documentation for subprocess.Popen(). - executable (string) See documentation for subprocess.Popen().
Default is None. Default is None.
''' '''
if isinstance(args, list):
shell = False shell = False
elif isinstance(args, basestring): if isinstance(args, list):
pass
elif isinstance(args, basestring) and use_unsafe_shell:
shell = True shell = True
elif isinstance(args, basestring):
args = shlex.split(args)
else: else:
msg = "Argument 'args' to run_command must be list or string" msg = "Argument 'args' to run_command must be list or string"
self.fail_json(rc=257, cmd=args, msg=msg) self.fail_json(rc=257, cmd=args, msg=msg)
rc = 0 rc = 0
msg = None msg = None
st_in = None st_in = None
@ -1022,24 +1028,24 @@ class AnsibleModule(object):
if data: if data:
st_in = subprocess.PIPE st_in = subprocess.PIPE
kwargs = dict(
executable=executable,
shell=shell,
close_fds=close_fds,
stdin= st_in,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
if path_prefix:
kwargs['env'] = env
if cwd:
kwargs['cwd'] = cwd
try: try:
if path_prefix is not None: cmd = subprocess.Popen(args, **kwargs)
cmd = subprocess.Popen(args,
executable=executable,
shell=shell,
close_fds=close_fds,
stdin=st_in,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env)
else:
cmd = subprocess.Popen(args,
executable=executable,
shell=shell,
close_fds=close_fds,
stdin=st_in,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
if data: if data:
if not binary_data: if not binary_data:

View file

@ -0,0 +1,252 @@
import os
import re
import types
import ConfigParser
import shlex
class RegistrationBase(object):
def __init__(self, module, username=None, password=None):
self.module = module
self.username = username
self.password = password
def configure(self):
raise NotImplementedError("Must be implemented by a sub-class")
def enable(self):
# Remove any existing redhat.repo
redhat_repo = '/etc/yum.repos.d/redhat.repo'
if os.path.isfile(redhat_repo):
os.unlink(redhat_repo)
def register(self):
raise NotImplementedError("Must be implemented by a sub-class")
def unregister(self):
raise NotImplementedError("Must be implemented by a sub-class")
def unsubscribe(self):
raise NotImplementedError("Must be implemented by a sub-class")
def update_plugin_conf(self, plugin, enabled=True):
plugin_conf = '/etc/yum/pluginconf.d/%s.conf' % plugin
if os.path.isfile(plugin_conf):
cfg = ConfigParser.ConfigParser()
cfg.read([plugin_conf])
if enabled:
cfg.set('main', 'enabled', 1)
else:
cfg.set('main', 'enabled', 0)
fd = open(plugin_conf, 'rwa+')
cfg.write(fd)
fd.close()
def subscribe(self, **kwargs):
raise NotImplementedError("Must be implemented by a sub-class")
class Rhsm(RegistrationBase):
def __init__(self, module, username=None, password=None):
RegistrationBase.__init__(self, module, username, password)
self.config = self._read_config()
self.module = module
def _read_config(self, rhsm_conf='/etc/rhsm/rhsm.conf'):
'''
Load RHSM configuration from /etc/rhsm/rhsm.conf.
Returns:
* ConfigParser object
'''
# Read RHSM defaults ...
cp = ConfigParser.ConfigParser()
cp.read(rhsm_conf)
# Add support for specifying a default value w/o having to standup some configuration
# Yeah, I know this should be subclassed ... but, oh well
def get_option_default(self, key, default=''):
sect, opt = key.split('.', 1)
if self.has_section(sect) and self.has_option(sect, opt):
return self.get(sect, opt)
else:
return default
cp.get_option = types.MethodType(get_option_default, cp, ConfigParser.ConfigParser)
return cp
def enable(self):
'''
Enable the system to receive updates from subscription-manager.
This involves updating affected yum plugins and removing any
conflicting yum repositories.
'''
RegistrationBase.enable(self)
self.update_plugin_conf('rhnplugin', False)
self.update_plugin_conf('subscription-manager', True)
def configure(self, **kwargs):
'''
Configure the system as directed for registration with RHN
Raises:
* Exception - if error occurs while running command
'''
args = ['subscription-manager', 'config']
# Pass supplied **kwargs as parameters to subscription-manager. Ignore
# non-configuration parameters and replace '_' with '.'. For example,
# 'server_hostname' becomes '--system.hostname'.
for k,v in kwargs.items():
if re.search(r'^(system|rhsm)_', k):
args.append('--%s=%s' % (k.replace('_','.'), v))
self.module.run_command(args, check_rc=True)
@property
def is_registered(self):
'''
Determine whether the current system
Returns:
* Boolean - whether the current system is currently registered to
RHN.
'''
# Quick version...
if False:
return os.path.isfile('/etc/pki/consumer/cert.pem') and \
os.path.isfile('/etc/pki/consumer/key.pem')
args = ['subscription-manager', 'identity']
rc, stdout, stderr = self.module.run_command(args, check_rc=False)
if rc == 0:
return True
else:
return False
def register(self, username, password, autosubscribe, activationkey):
'''
Register the current system to the provided RHN server
Raises:
* Exception - if error occurs while running command
'''
args = ['subscription-manager', 'register']
# Generate command arguments
if activationkey:
args.append('--activationkey "%s"' % activationkey)
else:
if autosubscribe:
args.append('--autosubscribe')
if username:
args.extend(['--username', username])
if password:
args.extend(['--password', password])
# Do the needful...
rc, stderr, stdout = self.module.run_command(args, check_rc=True)
def unsubscribe(self):
'''
Unsubscribe a system from all subscribed channels
Raises:
* Exception - if error occurs while running command
'''
args = ['subscription-manager', 'unsubscribe', '--all']
rc, stderr, stdout = self.module.run_command(args, check_rc=True)
def unregister(self):
'''
Unregister a currently registered system
Raises:
* Exception - if error occurs while running command
'''
args = ['subscription-manager', 'unregister']
rc, stderr, stdout = self.module.run_command(args, check_rc=True)
def subscribe(self, regexp):
'''
Subscribe current system to available pools matching the specified
regular expression
Raises:
* Exception - if error occurs while running command
'''
# Available pools ready for subscription
available_pools = RhsmPools(self.module)
for pool in available_pools.filter(regexp):
pool.subscribe()
class RhsmPool(object):
'''
Convenience class for housing subscription information
'''
def __init__(self, module, **kwargs):
self.module = module
for k,v in kwargs.items():
setattr(self, k, v)
def __str__(self):
return str(self.__getattribute__('_name'))
def subscribe(self):
args = "subscription-manager subscribe --pool %s" % self.PoolId
rc, stdout, stderr = self.module.run_command(args, check_rc=True)
if rc == 0:
return True
else:
return False
class RhsmPools(object):
"""
This class is used for manipulating pools subscriptions with RHSM
"""
def __init__(self, module):
self.module = module
self.products = self._load_product_list()
def __iter__(self):
return self.products.__iter__()
def _load_product_list(self):
"""
Loads list of all availaible pools for system in data structure
"""
args = "subscription-manager list --available"
rc, stdout, stderr = self.module.run_command(args, check_rc=True)
products = []
for line in stdout.split('\n'):
# Remove leading+trailing whitespace
line = line.strip()
# An empty line implies the end of a output group
if len(line) == 0:
continue
# If a colon ':' is found, parse
elif ':' in line:
(key, value) = line.split(':',1)
key = key.strip().replace(" ", "") # To unify
value = value.strip()
if key in ['ProductName', 'SubscriptionName']:
# Remember the name for later processing
products.append(RhsmPool(self.module, _name=value, key=value))
elif products:
# Associate value with most recently recorded product
products[-1].__setattr__(key, value)
# FIXME - log some warning?
#else:
# warnings.warn("Unhandled subscription key/value: %s/%s" % (key,value))
return products
def filter(self, regexp='^$'):
'''
Return a list of RhsmPools whose name matches the provided regular expression
'''
r = re.compile(regexp)
for product in self.products:
if r.search(product._name):
yield product

View file

@ -32,7 +32,7 @@ class LookupModule(object):
ret = [] ret = []
for term in terms: for term in terms:
p = subprocess.Popen(term, cwd=self.basedir, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p = subprocess.Popen(term, cwd=self.basedir, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
(stdout, stderr) = p.communicate() (stdout, stderr) = p.communicate()
if p.returncode == 0: if p.returncode == 0:
ret.append(stdout.decode("utf-8").rstrip()) ret.append(stdout.decode("utf-8").rstrip())

View file

@ -113,13 +113,14 @@ class VMNotFound(Exception):
class LibvirtConnection(object): class LibvirtConnection(object):
def __init__(self, uri): def __init__(self, uri, module):
cmd = subprocess.Popen("uname -r", shell=True, stdout=subprocess.PIPE, self.module = module
close_fds=True)
output = cmd.communicate()[0]
if output.find("xen") != -1: cmd = "uname -r"
rc, stdout, stderr = self.module.run_command(cmd)
if stdout.find("xen") != -1:
conn = libvirt.open(None) conn = libvirt.open(None)
else: else:
conn = libvirt.open(uri) conn = libvirt.open(uri)
@ -221,11 +222,12 @@ class LibvirtConnection(object):
class Virt(object): class Virt(object):
def __init__(self, uri): def __init__(self, uri, module):
self.module = module
self.uri = uri self.uri = uri
def __get_conn(self): def __get_conn(self):
self.conn = LibvirtConnection(self.uri) self.conn = LibvirtConnection(self.uri, self.module)
return self.conn return self.conn
def get_vm(self, vmid): def get_vm(self, vmid):
@ -399,7 +401,7 @@ def core(module):
uri = module.params.get('uri', None) uri = module.params.get('uri', None)
xml = module.params.get('xml', None) xml = module.params.get('xml', None)
v = Virt(uri) v = Virt(uri, module)
res = {} res = {}
if state and command=='list_vms': if state and command=='list_vms':

View file

@ -136,7 +136,7 @@ def main():
args = shlex.split(args) args = shlex.split(args)
startd = datetime.datetime.now() startd = datetime.datetime.now()
rc, out, err = module.run_command(args, executable=executable) rc, out, err = module.run_command(args, executable=executable, use_unsafe_shell=shell)
endd = datetime.datetime.now() endd = datetime.datetime.now()
delta = endd - startd delta = endd - startd

View file

@ -16,8 +16,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import subprocess
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: synchronize module: synchronize
@ -272,6 +270,13 @@ def main():
cmd = cmd + " --rsync-path '%s'" %(rsync_path) cmd = cmd + " --rsync-path '%s'" %(rsync_path)
changed_marker = '<<CHANGED>>' changed_marker = '<<CHANGED>>'
cmd = cmd + " --out-format='" + changed_marker + "%i %n%L'" cmd = cmd + " --out-format='" + changed_marker + "%i %n%L'"
# expand the paths
if '@' not in source:
source = os.path.expanduser(source)
if '@' not in dest:
dest = os.path.expanduser(dest)
cmd = ' '.join([cmd, source, dest]) cmd = ' '.join([cmd, source, dest])
cmdstr = cmd cmdstr = cmd
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd)

View file

@ -44,8 +44,6 @@ EXAMPLES = '''
- local_action: osx_say msg="{{inventory_hostname}} is all done" voice=Zarvox - local_action: osx_say msg="{{inventory_hostname}} is all done" voice=Zarvox
''' '''
import subprocess
DEFAULT_VOICE='Trinoids' DEFAULT_VOICE='Trinoids'
def say(module, msg, voice): def say(module, msg, voice):

View file

@ -151,8 +151,8 @@ def main():
command = '%s %s' % (virtualenv, env) command = '%s %s' % (virtualenv, env)
if site_packages: if site_packages:
command += ' --system-site-packages' command += ' --system-site-packages'
os.chdir(tempfile.gettempdir()) cwd = tempfile.gettempdir()
rc_venv, out_venv, err_venv = module.run_command(command) rc_venv, out_venv, err_venv = module.run_command(command, cwd=cwd)
rc += rc_venv rc += rc_venv
out += out_venv out += out_venv

View file

@ -125,10 +125,11 @@ class Npm(object):
cmd.append(self.name_version) cmd.append(self.name_version)
#If path is specified, cd into that path and run the command. #If path is specified, cd into that path and run the command.
cwd = None
if self.path: if self.path:
os.chdir(self.path) cwd = self.path
rc, out, err = self.module.run_command(cmd, check_rc=check_rc) rc, out, err = self.module.run_command(cmd, check_rc=check_rc, cwd=cwd)
return out return out
return '' return ''

View file

@ -90,7 +90,8 @@ def query_package(module, name, state="installed"):
# pacman -Q returns 0 if the package is installed, # pacman -Q returns 0 if the package is installed,
# 1 if it is not installed # 1 if it is not installed
if state == "installed": if state == "installed":
rc = os.system("pacman -Q %s" % (name)) cmd = "pacman -Q %s" % (name)
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
if rc == 0: if rc == 0:
return True return True
@ -99,7 +100,8 @@ def query_package(module, name, state="installed"):
def update_package_db(module): def update_package_db(module):
rc = os.system("pacman -Syy > /dev/null") cmd = "pacman -Syy > /dev/null"
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
if rc != 0: if rc != 0:
module.fail_json(msg="could not update package db") module.fail_json(msg="could not update package db")
@ -118,7 +120,8 @@ def remove_packages(module, packages):
if not query_package(module, package): if not query_package(module, package):
continue continue
rc = os.system("pacman -%s %s --noconfirm > /dev/null" % (args, package)) cmd = "pacman -%s %s --noconfirm > /dev/null" % (args, package)
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
if rc != 0: if rc != 0:
module.fail_json(msg="failed to remove %s" % (package)) module.fail_json(msg="failed to remove %s" % (package))
@ -145,7 +148,8 @@ def install_packages(module, packages, package_files):
else: else:
params = '-S %s' % package params = '-S %s' % package
rc = os.system("pacman %s --noconfirm > /dev/null" % (params)) cmd = "pacman %s --noconfirm > /dev/null" % (params)
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
if rc != 0: if rc != 0:
module.fail_json(msg="failed to install %s" % (package)) module.fail_json(msg="failed to install %s" % (package))

View file

@ -253,10 +253,10 @@ def main():
cmd = '%s --no-site-packages %s' % (virtualenv, env) cmd = '%s --no-site-packages %s' % (virtualenv, env)
else: else:
cmd = '%s %s' % (virtualenv, env) cmd = '%s %s' % (virtualenv, env)
os.chdir(tempfile.gettempdir()) this_dir = tempfile.gettempdir()
if chdir: if chdir:
os.chdir(chdir) this_dir = os.path.join(this_dir, chdir)
rc, out_venv, err_venv = module.run_command(cmd) rc, out_venv, err_venv = module.run_command(cmd, cwd=this_dir)
out += out_venv out += out_venv
err += err_venv err += err_venv
if rc != 0: if rc != 0:
@ -298,10 +298,11 @@ def main():
if module.check_mode: if module.check_mode:
module.exit_json(changed=True) module.exit_json(changed=True)
os.chdir(tempfile.gettempdir()) this_dir = tempfile.gettempdir()
if chdir: if chdir:
os.chdir(chdir) this_dir = os.path.join(this_dir, chdir)
rc, out_pip, err_pip = module.run_command(cmd, path_prefix=path_prefix)
rc, out_pip, err_pip = module.run_command(cmd, path_prefix=path_prefix, cwd=this_dir)
out += out_pip out += out_pip
err += err_pip err += err_pip
if rc == 1 and state == 'absent' and 'not installed' in out_pip: if rc == 1 and state == 'absent' and 'not installed' in out_pip:

View file

@ -75,39 +75,13 @@ EXAMPLES = '''
import os import os
import re import re
import types import types
import subprocess
import ConfigParser import ConfigParser
import shlex import shlex
class CommandException(Exception): class RegistrationBase(object):
pass def __init__(self, module, username=None, password=None):
self.module = module
def run_command(args):
'''
Convenience method to run a command, specified as a list of arguments.
Returns:
* tuple - (stdout, stder, retcode)
'''
# Coerce into a string
if isinstance(args, str):
args = shlex.split(args)
# Run desired command
proc = subprocess.Popen(args, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
(stdout, stderr) = proc.communicate()
returncode = proc.poll()
if returncode != 0:
cmd = ' '.join(args)
raise CommandException("Command failed (%s): %s\n%s" % (returncode, cmd, stdout))
return (stdout, stderr, returncode)
class RegistrationBase (object):
def __init__(self, username=None, password=None):
self.username = username self.username = username
self.password = password self.password = password
@ -147,9 +121,10 @@ class RegistrationBase (object):
class Rhsm(RegistrationBase): class Rhsm(RegistrationBase):
def __init__(self, username=None, password=None): def __init__(self, module, username=None, password=None):
RegistrationBase.__init__(self, username, password) RegistrationBase.__init__(self, module, username, password)
self.config = self._read_config() self.config = self._read_config()
self.module = module
def _read_config(self, rhsm_conf='/etc/rhsm/rhsm.conf'): def _read_config(self, rhsm_conf='/etc/rhsm/rhsm.conf'):
''' '''
@ -200,7 +175,7 @@ class Rhsm(RegistrationBase):
if re.search(r'^(system|rhsm)_', k): if re.search(r'^(system|rhsm)_', k):
args.append('--%s=%s' % (k.replace('_','.'), v)) args.append('--%s=%s' % (k.replace('_','.'), v))
run_command(args) self.module.run_command(args, check_rc=True)
@property @property
def is_registered(self): def is_registered(self):
@ -216,13 +191,11 @@ class Rhsm(RegistrationBase):
os.path.isfile('/etc/pki/consumer/key.pem') os.path.isfile('/etc/pki/consumer/key.pem')
args = ['subscription-manager', 'identity'] args = ['subscription-manager', 'identity']
try: rc, stdout, stderr = self.module.run_command(args, check_rc=False)
(stdout, stderr, retcode) = run_command(args) if rc == 0:
except CommandException, e:
return False
else:
# Display some debug output
return True return True
else:
return False
def register(self, username, password, autosubscribe, activationkey): def register(self, username, password, autosubscribe, activationkey):
''' '''
@ -244,7 +217,7 @@ class Rhsm(RegistrationBase):
args.extend(['--password', password]) args.extend(['--password', password])
# Do the needful... # Do the needful...
run_command(args) rc, stderr, stdout = self.module.run_command(args, check_rc=True)
def unsubscribe(self): def unsubscribe(self):
''' '''
@ -253,7 +226,7 @@ class Rhsm(RegistrationBase):
* Exception - if error occurs while running command * Exception - if error occurs while running command
''' '''
args = ['subscription-manager', 'unsubscribe', '--all'] args = ['subscription-manager', 'unsubscribe', '--all']
run_command(args) rc, stderr, stdout = self.module.run_command(args, check_rc=True)
def unregister(self): def unregister(self):
''' '''
@ -262,7 +235,7 @@ class Rhsm(RegistrationBase):
* Exception - if error occurs while running command * Exception - if error occurs while running command
''' '''
args = ['subscription-manager', 'unregister'] args = ['subscription-manager', 'unregister']
run_command(args) rc, stderr, stdout = self.module.run_command(args, check_rc=True)
def subscribe(self, regexp): def subscribe(self, regexp):
''' '''
@ -273,7 +246,7 @@ class Rhsm(RegistrationBase):
''' '''
# Available pools ready for subscription # Available pools ready for subscription
available_pools = RhsmPools() available_pools = RhsmPools(self.module)
for pool in available_pools.filter(regexp): for pool in available_pools.filter(regexp):
pool.subscribe() pool.subscribe()
@ -284,7 +257,8 @@ class RhsmPool(object):
Convenience class for housing subscription information Convenience class for housing subscription information
''' '''
def __init__(self, **kwargs): def __init__(self, module, **kwargs):
self.module = module
for k,v in kwargs.items(): for k,v in kwargs.items():
setattr(self, k, v) setattr(self, k, v)
@ -292,15 +266,20 @@ class RhsmPool(object):
return str(self.__getattribute__('_name')) return str(self.__getattribute__('_name'))
def subscribe(self): def subscribe(self):
(stdout, stderr, retcode) = run_command("subscription-manager subscribe --pool %s" % self.PoolId) args = "subscription-manager subscribe --pool %s" % self.PoolId
rc, stdout, stderr = self.module.run_command(args, check_rc=True)
if rc == 0:
return True return True
else:
return False
class RhsmPools(object): class RhsmPools(object):
""" """
This class is used for manipulating pools subscriptions with RHSM This class is used for manipulating pools subscriptions with RHSM
""" """
def __init__(self): def __init__(self, module):
self.module = module
self.products = self._load_product_list() self.products = self._load_product_list()
def __iter__(self): def __iter__(self):
@ -310,7 +289,8 @@ class RhsmPools(object):
""" """
Loads list of all availaible pools for system in data structure Loads list of all availaible pools for system in data structure
""" """
(stdout, stderr, retval) = run_command("subscription-manager list --available") args = "subscription-manager list --available"
rc, stdout, stderr = self.module.run_command(args, check_rc=True)
products = [] products = []
for line in stdout.split('\n'): for line in stdout.split('\n'):
@ -326,7 +306,7 @@ class RhsmPools(object):
value = value.strip() value = value.strip()
if key in ['ProductName', 'SubscriptionName']: if key in ['ProductName', 'SubscriptionName']:
# Remember the name for later processing # Remember the name for later processing
products.append(RhsmPool(_name=value, key=value)) products.append(RhsmPool(self.module, _name=value, key=value))
elif products: elif products:
# Associate value with most recently recorded product # Associate value with most recently recorded product
products[-1].__setattr__(key, value) products[-1].__setattr__(key, value)
@ -348,7 +328,7 @@ class RhsmPools(object):
def main(): def main():
# Load RHSM configuration from file # Load RHSM configuration from file
rhn = Rhsm() rhn = Rhsm(AnsibleModule())
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec = dict(
@ -364,6 +344,7 @@ def main():
) )
) )
rhn.module = module
state = module.params['state'] state = module.params['state']
username = module.params['username'] username = module.params['username']
password = module.params['password'] password = module.params['password']

View file

@ -72,12 +72,7 @@ EXAMPLES = '''
''' '''
import sys import sys
import os
import re
import types import types
import subprocess
import ConfigParser
import shlex
import xmlrpclib import xmlrpclib
import urlparse import urlparse
@ -90,75 +85,9 @@ except ImportError, e:
module.fail_json(msg="Unable to import up2date_client. Is 'rhn-client-tools' installed?\n%s" % e) module.fail_json(msg="Unable to import up2date_client. Is 'rhn-client-tools' installed?\n%s" % e)
class CommandException(Exception):
pass
def run_command(args):
'''
Convenience method to run a command, specified as a list of arguments.
Returns:
* tuple - (stdout, stder, retcode)
'''
# Coerce into a string
if isinstance(args, str):
args = shlex.split(args)
# Run desired command
proc = subprocess.Popen(args, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
(stdout, stderr) = proc.communicate()
returncode = proc.poll()
if returncode != 0:
cmd = ' '.join(args)
raise CommandException("Command failed (%s): %s\n%s" % (returncode, cmd, stdout))
return (stdout, stderr, returncode)
class RegistrationBase (object):
def __init__(self, username=None, password=None):
self.username = username
self.password = password
def configure(self):
raise NotImplementedError("Must be implemented by a sub-class")
def enable(self):
# Remove any existing redhat.repo
redhat_repo = '/etc/yum.repos.d/redhat.repo'
if os.path.isfile(redhat_repo):
os.unlink(redhat_repo)
def register(self):
raise NotImplementedError("Must be implemented by a sub-class")
def unregister(self):
raise NotImplementedError("Must be implemented by a sub-class")
def unsubscribe(self):
raise NotImplementedError("Must be implemented by a sub-class")
def update_plugin_conf(self, plugin, enabled=True):
plugin_conf = '/etc/yum/pluginconf.d/%s.conf' % plugin
if os.path.isfile(plugin_conf):
cfg = ConfigParser.ConfigParser()
cfg.read([plugin_conf])
if enabled:
cfg.set('main', 'enabled', 1)
else:
cfg.set('main', 'enabled', 0)
fd = open(plugin_conf, 'rwa+')
cfg.write(fd)
fd.close()
def subscribe(self, **kwargs):
raise NotImplementedError("Must be implemented by a sub-class")
class Rhn(RegistrationBase): class Rhn(RegistrationBase):
def __init__(self, username=None, password=None): def __init__(self, module, username=None, password=None):
RegistrationBase.__init__(self, username, password) RegistrationBase.__init__(self, username, password)
self.config = self.load_config() self.config = self.load_config()
@ -271,7 +200,7 @@ class Rhn(RegistrationBase):
register_cmd += " --activationkey '%s'" % activationkey register_cmd += " --activationkey '%s'" % activationkey
# FIXME - support --profilename # FIXME - support --profilename
# FIXME - support --systemorgid # FIXME - support --systemorgid
run_command(register_cmd) rc, stdout, stderr = self.module.run_command(register_command, check_rc=True)
def api(self, method, *args): def api(self, method, *args):
''' '''
@ -309,14 +238,14 @@ class Rhn(RegistrationBase):
Subscribe to requested yum repositories using 'rhn-channel' command Subscribe to requested yum repositories using 'rhn-channel' command
''' '''
rhn_channel_cmd = "rhn-channel --user='%s' --password='%s'" % (self.username, self.password) rhn_channel_cmd = "rhn-channel --user='%s' --password='%s'" % (self.username, self.password)
(stdout, stderr, rc) = run_command(rhn_channel_cmd + " --available-channels") rc, stdout, stderr = self.module.run_command(rhn_channel_cmd + " --available-channels", check_rc=True)
# Enable requested repoid's # Enable requested repoid's
for wanted_channel in channels: for wanted_channel in channels:
# Each inserted repo regexp will be matched. If no match, no success. # Each inserted repo regexp will be matched. If no match, no success.
for availaible_channel in stdout.rstrip().split('\n'): # .rstrip() because of \n at the end -> empty string at the end for availaible_channel in stdout.rstrip().split('\n'): # .rstrip() because of \n at the end -> empty string at the end
if re.search(wanted_repo, available_channel): if re.search(wanted_repo, available_channel):
run_command(rhn_channel_cmd + " --add --channel=%s" % available_channel) rc, stdout, stderr = self.module.run_command(rhn_channel_cmd + " --add --channel=%s" % available_channel, check_rc=True)
def main(): def main():
@ -379,4 +308,6 @@ def main():
# import module snippets # import module snippets
from ansible.module_utils.basic import * from ansible.module_utils.basic import *
from ansible.module_utils.redhat import *
main() main()

View file

@ -91,7 +91,8 @@ def query_package(module, name):
# rpm -q returns 0 if the package is installed, # rpm -q returns 0 if the package is installed,
# 1 if it is not installed # 1 if it is not installed
rc = os.system("rpm -q %s" % (name)) cmd = "rpm -q %s" % (name)
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
if rc == 0: if rc == 0:
return True return True
else: else:
@ -103,13 +104,14 @@ def query_package_provides(module, name):
# rpm -q returns 0 if the package is installed, # rpm -q returns 0 if the package is installed,
# 1 if it is not installed # 1 if it is not installed
rc = os.system("rpm -q --provides %s >/dev/null" % (name)) cmd = "rpm -q --provides %s >/dev/null" % (name)
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
return rc == 0 return rc == 0
def update_package_db(module): def update_package_db(module):
rc = os.system("urpmi.update -a -q") cmd = "urpmi.update -a -q"
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
if rc != 0: if rc != 0:
module.fail_json(msg="could not update package db") module.fail_json(msg="could not update package db")
@ -123,7 +125,8 @@ def remove_packages(module, packages):
if not query_package(module, package): if not query_package(module, package):
continue continue
rc = os.system("%s --auto %s > /dev/null" % (URPME_PATH, package)) cmd = "%s --auto %s > /dev/null" % (URPME_PATH, package)
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
if rc != 0: if rc != 0:
module.fail_json(msg="failed to remove %s" % (package)) module.fail_json(msg="failed to remove %s" % (package))

View file

@ -75,16 +75,17 @@ class Bzr(object):
self.version = version self.version = version
self.bzr_path = bzr_path self.bzr_path = bzr_path
def _command(self, args_list, **kwargs): def _command(self, args_list, cwd=None, **kwargs):
(rc, out, err) = self.module.run_command( (rc, out, err) = self.module.run_command(
[self.bzr_path] + args_list, **kwargs) [self.bzr_path] + args_list, cwd=cwd, **kwargs)
return (rc, out, err) return (rc, out, err)
def get_version(self): def get_version(self):
'''samples the version of the bzr branch''' '''samples the version of the bzr branch'''
os.chdir(self.dest)
cmd = "%s revno" % self.bzr_path cmd = "%s revno" % self.bzr_path
revno = os.popen(cmd).read().strip() rc, stdout, stderr = self.module.run_command(cmd, cwd=self.dest)
revno = stdout.strip()
return revno return revno
def clone(self): def clone(self):
@ -94,17 +95,18 @@ class Bzr(object):
os.makedirs(dest_dirname) os.makedirs(dest_dirname)
except: except:
pass pass
os.chdir(dest_dirname)
if self.version.lower() != 'head': if self.version.lower() != 'head':
args_list = ["branch", "-r", self.version, self.parent, self.dest] args_list = ["branch", "-r", self.version, self.parent, self.dest]
else: else:
args_list = ["branch", self.parent, self.dest] args_list = ["branch", self.parent, self.dest]
return self._command(args_list, check_rc=True) return self._command(args_list, check_rc=True, cwd=dest_dirname)
def has_local_mods(self): def has_local_mods(self):
os.chdir(self.dest)
cmd = "%s status -S" % self.bzr_path cmd = "%s status -S" % self.bzr_path
lines = os.popen(cmd).read().splitlines() rc, stdout, stderr = self.module.run_command(cmd, cwd=self.dest)
lines = stdout.splitlines()
lines = filter(lambda c: not re.search('^\\?\\?.*$', c), lines) lines = filter(lambda c: not re.search('^\\?\\?.*$', c), lines)
return len(lines) > 0 return len(lines) > 0
@ -114,30 +116,27 @@ class Bzr(object):
Discards any changes to tracked files in the working Discards any changes to tracked files in the working
tree since that commit. tree since that commit.
''' '''
os.chdir(self.dest)
if not force and self.has_local_mods(): if not force and self.has_local_mods():
self.module.fail_json(msg="Local modifications exist in branch (force=no).") self.module.fail_json(msg="Local modifications exist in branch (force=no).")
return self._command(["revert"], check_rc=True) return self._command(["revert"], check_rc=True, cwd=self.dest)
def fetch(self): def fetch(self):
'''updates branch from remote sources''' '''updates branch from remote sources'''
os.chdir(self.dest)
if self.version.lower() != 'head': if self.version.lower() != 'head':
(rc, out, err) = self._command(["pull", "-r", self.version]) (rc, out, err) = self._command(["pull", "-r", self.version], cwd=self.dest)
else: else:
(rc, out, err) = self._command(["pull"]) (rc, out, err) = self._command(["pull"], cwd=self.dest)
if rc != 0: if rc != 0:
self.module.fail_json(msg="Failed to pull") self.module.fail_json(msg="Failed to pull")
return (rc, out, err) return (rc, out, err)
def switch_version(self): def switch_version(self):
'''once pulled, switch to a particular revno or revid''' '''once pulled, switch to a particular revno or revid'''
os.chdir(self.dest)
if self.version.lower() != 'head': if self.version.lower() != 'head':
args_list = ["revert", "-r", self.version] args_list = ["revert", "-r", self.version]
else: else:
args_list = ["revert"] args_list = ["revert"]
return self._command(args_list, check_rc=True) return self._command(args_list, check_rc=True, cwd=self.dest)
# =========================================== # ===========================================

View file

@ -181,11 +181,12 @@ def set_git_ssh(ssh_wrapper, key_file, ssh_opts):
if ssh_opts: if ssh_opts:
os.environ["GIT_SSH_OPTS"] = ssh_opts os.environ["GIT_SSH_OPTS"] = ssh_opts
def get_version(git_path, dest, ref="HEAD"): def get_version(module, git_path, dest, ref="HEAD"):
''' samples the version of the git repo ''' ''' samples the version of the git repo '''
os.chdir(dest)
cmd = "%s rev-parse %s" % (git_path, ref) cmd = "%s rev-parse %s" % (git_path, ref)
sha = os.popen(cmd).read().rstrip("\n") rc, stdout, stderr = module.run_command(cmd, cwd=dest)
sha = stdout.rstrip('\n')
return sha return sha
def clone(git_path, module, repo, dest, remote, depth, version, bare, reference): def clone(git_path, module, repo, dest, remote, depth, version, bare, reference):
@ -195,7 +196,6 @@ def clone(git_path, module, repo, dest, remote, depth, version, bare, reference)
os.makedirs(dest_dirname) os.makedirs(dest_dirname)
except: except:
pass pass
os.chdir(dest_dirname)
cmd = [ git_path, 'clone' ] cmd = [ git_path, 'clone' ]
if bare: if bare:
cmd.append('--bare') cmd.append('--bare')
@ -209,19 +209,19 @@ def clone(git_path, module, repo, dest, remote, depth, version, bare, reference)
if reference: if reference:
cmd.extend([ '--reference', str(reference) ]) cmd.extend([ '--reference', str(reference) ])
cmd.extend([ repo, dest ]) cmd.extend([ repo, dest ])
module.run_command(cmd, check_rc=True) module.run_command(cmd, check_rc=True, cwd=dest_dirname)
if bare: if bare:
os.chdir(dest)
if remote != 'origin': if remote != 'origin':
module.run_command([git_path, 'remote', 'add', remote, repo], check_rc=True) module.run_command([git_path, 'remote', 'add', remote, repo], check_rc=True, cwd=dest)
def has_local_mods(git_path, dest, bare): def has_local_mods(module, git_path, dest, bare):
if bare: if bare:
return False return False
os.chdir(dest)
cmd = "%s status -s" % (git_path,) cmd = "%s status -s" % (git_path)
lines = os.popen(cmd).read().splitlines() rc, stdout, stderr = module.run_command(cmd, cwd=dest)
lines = filter(lambda c: not re.search('^\\?\\?.*$', c), lines) lines = stdout.splitlines()
return len(lines) > 0 return len(lines) > 0
def reset(git_path, module, dest): def reset(git_path, module, dest):
@ -230,16 +230,16 @@ def reset(git_path, module, dest):
Discards any changes to tracked files in working Discards any changes to tracked files in working
tree since that commit. tree since that commit.
''' '''
os.chdir(dest)
cmd = "%s reset --hard HEAD" % (git_path,) cmd = "%s reset --hard HEAD" % (git_path,)
return module.run_command(cmd, check_rc=True) return module.run_command(cmd, check_rc=True, cwd=dest)
def get_remote_head(git_path, module, dest, version, remote, bare): def get_remote_head(git_path, module, dest, version, remote, bare):
cloning = False cloning = False
cwd = None
if remote == module.params['repo']: if remote == module.params['repo']:
cloning = True cloning = True
else: else:
os.chdir(dest) cwd = dest
if version == 'HEAD': if version == 'HEAD':
if cloning: if cloning:
# cloning the repo, just get the remote's HEAD version # cloning the repo, just get the remote's HEAD version
@ -255,7 +255,7 @@ def get_remote_head(git_path, module, dest, version, remote, bare):
# appears to be a sha1. return as-is since it appears # appears to be a sha1. return as-is since it appears
# cannot check for a specific sha1 on remote # cannot check for a specific sha1 on remote
return version return version
(rc, out, err) = module.run_command(cmd, check_rc=True ) (rc, out, err) = module.run_command(cmd, check_rc=True, cwd=cwd)
if len(out) < 1: if len(out) < 1:
module.fail_json(msg="Could not determine remote revision for %s" % version) module.fail_json(msg="Could not determine remote revision for %s" % version)
rev = out.split()[0] rev = out.split()[0]
@ -270,10 +270,9 @@ def is_remote_tag(git_path, module, dest, remote, version):
return False return False
def get_branches(git_path, module, dest): def get_branches(git_path, module, dest):
os.chdir(dest)
branches = [] branches = []
cmd = '%s branch -a' % (git_path,) cmd = '%s branch -a' % (git_path,)
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd, cwd=dest)
if rc != 0: if rc != 0:
module.fail_json(msg="Could not determine branch data - received %s" % out) module.fail_json(msg="Could not determine branch data - received %s" % out)
for line in out.split('\n'): for line in out.split('\n'):
@ -281,10 +280,9 @@ def get_branches(git_path, module, dest):
return branches return branches
def get_tags(git_path, module, dest): def get_tags(git_path, module, dest):
os.chdir(dest)
tags = [] tags = []
cmd = '%s tag' % (git_path,) cmd = '%s tag' % (git_path,)
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd, cwd=dest)
if rc != 0: if rc != 0:
module.fail_json(msg="Could not determine tag data - received %s" % out) module.fail_json(msg="Could not determine tag data - received %s" % out)
for line in out.split('\n'): for line in out.split('\n'):
@ -352,18 +350,17 @@ def get_head_branch(git_path, module, dest, remote, bare=False):
def fetch(git_path, module, repo, dest, version, remote, bare): def fetch(git_path, module, repo, dest, version, remote, bare):
''' updates repo from remote sources ''' ''' updates repo from remote sources '''
os.chdir(dest)
if bare: if bare:
(rc, out1, err1) = module.run_command([git_path, 'fetch', remote, '+refs/heads/*:refs/heads/*']) (rc, out1, err1) = module.run_command([git_path, 'fetch', remote, '+refs/heads/*:refs/heads/*'], cwd=dest)
else: else:
(rc, out1, err1) = module.run_command("%s fetch %s" % (git_path, remote)) (rc, out1, err1) = module.run_command("%s fetch %s" % (git_path, remote), cwd=dest)
if rc != 0: if rc != 0:
module.fail_json(msg="Failed to download remote objects and refs") module.fail_json(msg="Failed to download remote objects and refs")
if bare: if bare:
(rc, out2, err2) = module.run_command([git_path, 'fetch', remote, '+refs/tags/*:refs/tags/*']) (rc, out2, err2) = module.run_command([git_path, 'fetch', remote, '+refs/tags/*:refs/tags/*'], cwd=dest)
else: else:
(rc, out2, err2) = module.run_command("%s fetch --tags %s" % (git_path, remote)) (rc, out2, err2) = module.run_command("%s fetch --tags %s" % (git_path, remote), cwd=dest)
if rc != 0: if rc != 0:
module.fail_json(msg="Failed to download remote objects and refs") module.fail_json(msg="Failed to download remote objects and refs")
(rc, out3, err3) = submodule_update(git_path, module, dest) (rc, out3, err3) = submodule_update(git_path, module, dest)
@ -371,28 +368,26 @@ def fetch(git_path, module, repo, dest, version, remote, bare):
def submodule_update(git_path, module, dest): def submodule_update(git_path, module, dest):
''' init and update any submodules ''' ''' init and update any submodules '''
os.chdir(dest)
# skip submodule commands if .gitmodules is not present # skip submodule commands if .gitmodules is not present
if not os.path.exists(os.path.join(dest, '.gitmodules')): if not os.path.exists(os.path.join(dest, '.gitmodules')):
return (0, '', '') return (0, '', '')
cmd = [ git_path, 'submodule', 'sync' ] cmd = [ git_path, 'submodule', 'sync' ]
(rc, out, err) = module.run_command(cmd, check_rc=True) (rc, out, err) = module.run_command(cmd, check_rc=True, cwd=dest)
cmd = [ git_path, 'submodule', 'update', '--init', '--recursive' ] cmd = [ git_path, 'submodule', 'update', '--init', '--recursive' ]
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd, cwd=dest)
if rc != 0: if rc != 0:
module.fail_json(msg="Failed to init/update submodules") module.fail_json(msg="Failed to init/update submodules")
return (rc, out, err) return (rc, out, err)
def switch_version(git_path, module, dest, remote, version): def switch_version(git_path, module, dest, remote, version):
''' once pulled, switch to a particular SHA, tag, or branch ''' ''' once pulled, switch to a particular SHA, tag, or branch '''
os.chdir(dest)
cmd = '' cmd = ''
if version != 'HEAD': if version != 'HEAD':
if is_remote_branch(git_path, module, dest, remote, version): if is_remote_branch(git_path, module, dest, remote, version):
if not is_local_branch(git_path, module, dest, version): if not is_local_branch(git_path, module, dest, version):
cmd = "%s checkout --track -b %s %s/%s" % (git_path, version, remote, version) cmd = "%s checkout --track -b %s %s/%s" % (git_path, version, remote, version)
else: else:
(rc, out, err) = module.run_command("%s checkout --force %s" % (git_path, version)) (rc, out, err) = module.run_command("%s checkout --force %s" % (git_path, version), cwd=dest)
if rc != 0: if rc != 0:
module.fail_json(msg="Failed to checkout branch %s" % version) module.fail_json(msg="Failed to checkout branch %s" % version)
cmd = "%s reset --hard %s/%s" % (git_path, remote, version) cmd = "%s reset --hard %s/%s" % (git_path, remote, version)
@ -400,11 +395,11 @@ def switch_version(git_path, module, dest, remote, version):
cmd = "%s checkout --force %s" % (git_path, version) cmd = "%s checkout --force %s" % (git_path, version)
else: else:
branch = get_head_branch(git_path, module, dest, remote) branch = get_head_branch(git_path, module, dest, remote)
(rc, out, err) = module.run_command("%s checkout --force %s" % (git_path, branch)) (rc, out, err) = module.run_command("%s checkout --force %s" % (git_path, branch), cwd=dest)
if rc != 0: if rc != 0:
module.fail_json(msg="Failed to checkout branch %s" % branch) module.fail_json(msg="Failed to checkout branch %s" % branch)
cmd = "%s reset --hard %s" % (git_path, remote) cmd = "%s reset --hard %s" % (git_path, remote)
(rc, out1, err1) = module.run_command(cmd) (rc, out1, err1) = module.run_command(cmd, cwd=dest)
if rc != 0: if rc != 0:
if version != 'HEAD': if version != 'HEAD':
module.fail_json(msg="Failed to checkout %s" % (version)) module.fail_json(msg="Failed to checkout %s" % (version))
@ -484,12 +479,12 @@ def main():
# Just return having found a repo already in the dest path # Just return having found a repo already in the dest path
# this does no checking that the repo is the actual repo # this does no checking that the repo is the actual repo
# requested. # requested.
before = get_version(git_path, dest) before = get_version(module, git_path, dest)
module.exit_json(changed=False, before=before, after=before) module.exit_json(changed=False, before=before, after=before)
else: else:
# else do a pull # else do a pull
local_mods = has_local_mods(git_path, dest, bare) local_mods = has_local_mods(module, git_path, dest, bare)
before = get_version(git_path, dest) before = get_version(module, git_path, dest)
if local_mods: if local_mods:
# failure should happen regardless of check mode # failure should happen regardless of check mode
if not force: if not force:
@ -519,7 +514,7 @@ def main():
switch_version(git_path, module, dest, remote, version) switch_version(git_path, module, dest, remote, version)
# determine if we changed anything # determine if we changed anything
after = get_version(git_path, dest) after = get_version(module, git_path, dest)
changed = False changed = False
if before != after or local_mods: if before != after or local_mods:

View file

@ -207,7 +207,9 @@ class Service(object):
os._exit(0) os._exit(0)
# Start the command # Start the command
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=lambda: os.close(pipe[1])) if isinstance(cmd, basestring):
cmd = shlex.split(cmd)
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=lambda: os.close(pipe[1]))
stdout = "" stdout = ""
stderr = "" stderr = ""
fds = [p.stdout, p.stderr] fds = [p.stdout, p.stderr]

View file

@ -29,7 +29,6 @@ import socket
import struct import struct
import datetime import datetime
import getpass import getpass
import subprocess
import ConfigParser import ConfigParser
import StringIO import StringIO
@ -1430,7 +1429,8 @@ class LinuxNetwork(Network):
""" """
platform = 'Linux' platform = 'Linux'
def __init__(self): def __init__(self, module):
self.module = module
Network.__init__(self) Network.__init__(self)
def populate(self): def populate(self):
@ -1616,12 +1616,15 @@ class LinuxNetwork(Network):
ips['all_ipv6_addresses'].append(address) ips['all_ipv6_addresses'].append(address)
ip_path = module.get_bin_path("ip") ip_path = module.get_bin_path("ip")
primary_data = subprocess.Popen(
[ip_path, 'addr', 'show', 'primary', device], args = [ip_path, 'addr', 'show', 'primary', device]
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] rc, stdout, stderr = self.module.run_command(args)
secondary_data = subprocess.Popen( primary_data = stdout
[ip_path, 'addr', 'show', 'secondary', device],
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] args = [ip_path, 'addr', 'show', 'secondary', device]
rc, stdout, stderr = self.module.run_command(args)
secondary_data = stdout
parse_ip_output(primary_data) parse_ip_output(primary_data)
parse_ip_output(secondary_data, secondary=True) parse_ip_output(secondary_data, secondary=True)
@ -2281,11 +2284,11 @@ def get_file_content(path, default=None):
data = default data = default
return data return data
def ansible_facts(): def ansible_facts(module):
facts = {} facts = {}
facts.update(Facts().populate()) facts.update(Facts().populate())
facts.update(Hardware().populate()) facts.update(Hardware().populate())
facts.update(Network().populate()) facts.update(Network(module).populate())
facts.update(Virtual().populate()) facts.update(Virtual().populate())
return facts return facts
@ -2294,7 +2297,7 @@ def ansible_facts():
def run_setup(module): def run_setup(module):
setup_options = {} setup_options = {}
facts = ansible_facts() facts = ansible_facts(module)
for (k, v) in facts.items(): for (k, v) in facts.items():
setup_options["ansible_%s" % k.replace('-', '_')] = v setup_options["ansible_%s" % k.replace('-', '_')] = v

View file

@ -232,7 +232,6 @@ def main():
_ensure_virtualenv(module) _ensure_virtualenv(module)
os.chdir(app_path)
cmd = "python manage.py %s" % (command, ) cmd = "python manage.py %s" % (command, )
if command in noinput_commands: if command in noinput_commands:
@ -251,7 +250,7 @@ def main():
if module.params[param]: if module.params[param]:
cmd = '%s %s' % (cmd, module.params[param]) cmd = '%s %s' % (cmd, module.params[param])
rc, out, err = module.run_command(cmd) rc, out, err = module.run_command(cmd, cwd=app_path)
if rc != 0: if rc != 0:
if command == 'createcachetable' and 'table' in err and 'already exists' in err: if command == 'createcachetable' and 'table' in err and 'already exists' in err:
out = 'Already exists.' out = 'Already exists.'