diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
index fd0b2edfc3b..67ceb3b605c 100644
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -990,12 +990,13 @@ class AnsibleModule(object):
# rename might not preserve context
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.
args is the command to run
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:
- check_rc (boolean) Whether to call fail_json in case of
non zero RC. Default is False.
@@ -1004,13 +1005,18 @@ class AnsibleModule(object):
- executable (string) See documentation for subprocess.Popen().
Default is None.
'''
+
+ shell = False
if isinstance(args, list):
- shell = False
- elif isinstance(args, basestring):
+ pass
+ elif isinstance(args, basestring) and use_unsafe_shell:
shell = True
+ elif isinstance(args, basestring):
+ args = shlex.split(args)
else:
msg = "Argument 'args' to run_command must be list or string"
self.fail_json(rc=257, cmd=args, msg=msg)
+
rc = 0
msg = None
st_in = None
@@ -1022,25 +1028,25 @@ class AnsibleModule(object):
if data:
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:
- if path_prefix is not None:
- 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)
-
+ cmd = subprocess.Popen(args, **kwargs)
+
if data:
if not binary_data:
data += '\\n'
diff --git a/lib/ansible/module_utils/redhat.py b/lib/ansible/module_utils/redhat.py
new file mode 100644
index 00000000000..a1081f9c8c7
--- /dev/null
+++ b/lib/ansible/module_utils/redhat.py
@@ -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
+
diff --git a/lib/ansible/runner/lookup_plugins/pipe.py b/lib/ansible/runner/lookup_plugins/pipe.py
index 4205b887ffe..62ec7e129ed 100644
--- a/lib/ansible/runner/lookup_plugins/pipe.py
+++ b/lib/ansible/runner/lookup_plugins/pipe.py
@@ -32,7 +32,7 @@ class LookupModule(object):
ret = []
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()
if p.returncode == 0:
ret.append(stdout.decode("utf-8").rstrip())
diff --git a/library/cloud/virt b/library/cloud/virt
index 42e99209b14..8cbf7fc895a 100644
--- a/library/cloud/virt
+++ b/library/cloud/virt
@@ -113,13 +113,14 @@ class VMNotFound(Exception):
class LibvirtConnection(object):
- def __init__(self, uri):
+ def __init__(self, uri, module):
- cmd = subprocess.Popen("uname -r", shell=True, stdout=subprocess.PIPE,
- close_fds=True)
- output = cmd.communicate()[0]
+ self.module = module
- 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)
else:
conn = libvirt.open(uri)
@@ -221,11 +222,12 @@ class LibvirtConnection(object):
class Virt(object):
- def __init__(self, uri):
+ def __init__(self, uri, module):
+ self.module = module
self.uri = uri
def __get_conn(self):
- self.conn = LibvirtConnection(self.uri)
+ self.conn = LibvirtConnection(self.uri, self.module)
return self.conn
def get_vm(self, vmid):
@@ -399,7 +401,7 @@ def core(module):
uri = module.params.get('uri', None)
xml = module.params.get('xml', None)
- v = Virt(uri)
+ v = Virt(uri, module)
res = {}
if state and command=='list_vms':
diff --git a/library/commands/command b/library/commands/command
index 76d2f828d0c..ba9ae30a7f2 100644
--- a/library/commands/command
+++ b/library/commands/command
@@ -136,7 +136,7 @@ def main():
args = shlex.split(args)
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()
delta = endd - startd
diff --git a/library/files/synchronize b/library/files/synchronize
index 493322393bc..eb556c30f53 100644
--- a/library/files/synchronize
+++ b/library/files/synchronize
@@ -16,8 +16,6 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
-import subprocess
-
DOCUMENTATION = '''
---
module: synchronize
@@ -272,6 +270,13 @@ def main():
cmd = cmd + " --rsync-path '%s'" %(rsync_path)
changed_marker = '<>'
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])
cmdstr = cmd
(rc, out, err) = module.run_command(cmd)
diff --git a/library/notification/osx_say b/library/notification/osx_say
index de5d1917c5f..39e3da88c19 100644
--- a/library/notification/osx_say
+++ b/library/notification/osx_say
@@ -44,8 +44,6 @@ EXAMPLES = '''
- local_action: osx_say msg="{{inventory_hostname}} is all done" voice=Zarvox
'''
-import subprocess
-
DEFAULT_VOICE='Trinoids'
def say(module, msg, voice):
diff --git a/library/packaging/easy_install b/library/packaging/easy_install
index bdacf8e464b..889a81f025a 100644
--- a/library/packaging/easy_install
+++ b/library/packaging/easy_install
@@ -151,8 +151,8 @@ def main():
command = '%s %s' % (virtualenv, env)
if site_packages:
command += ' --system-site-packages'
- os.chdir(tempfile.gettempdir())
- rc_venv, out_venv, err_venv = module.run_command(command)
+ cwd = tempfile.gettempdir()
+ rc_venv, out_venv, err_venv = module.run_command(command, cwd=cwd)
rc += rc_venv
out += out_venv
diff --git a/library/packaging/npm b/library/packaging/npm
index 62179c373aa..c623b6f7e6d 100644
--- a/library/packaging/npm
+++ b/library/packaging/npm
@@ -125,10 +125,11 @@ class Npm(object):
cmd.append(self.name_version)
#If path is specified, cd into that path and run the command.
+ cwd = None
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 ''
diff --git a/library/packaging/pacman b/library/packaging/pacman
index 3080cb4a607..a4a24ca5fd1 100644
--- a/library/packaging/pacman
+++ b/library/packaging/pacman
@@ -90,7 +90,8 @@ def query_package(module, name, state="installed"):
# pacman -Q returns 0 if the package is installed,
# 1 if it is not 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:
return True
@@ -99,7 +100,8 @@ def query_package(module, name, state="installed"):
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:
module.fail_json(msg="could not update package db")
@@ -118,7 +120,8 @@ def remove_packages(module, packages):
if not query_package(module, package):
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:
module.fail_json(msg="failed to remove %s" % (package))
@@ -145,7 +148,8 @@ def install_packages(module, packages, package_files):
else:
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:
module.fail_json(msg="failed to install %s" % (package))
diff --git a/library/packaging/pip b/library/packaging/pip
index 35487c32963..aa55bf8ba0b 100644
--- a/library/packaging/pip
+++ b/library/packaging/pip
@@ -253,10 +253,10 @@ def main():
cmd = '%s --no-site-packages %s' % (virtualenv, env)
else:
cmd = '%s %s' % (virtualenv, env)
- os.chdir(tempfile.gettempdir())
+ this_dir = tempfile.gettempdir()
if chdir:
- os.chdir(chdir)
- rc, out_venv, err_venv = module.run_command(cmd)
+ this_dir = os.path.join(this_dir, chdir)
+ rc, out_venv, err_venv = module.run_command(cmd, cwd=this_dir)
out += out_venv
err += err_venv
if rc != 0:
@@ -298,10 +298,11 @@ def main():
if module.check_mode:
module.exit_json(changed=True)
- os.chdir(tempfile.gettempdir())
+ this_dir = tempfile.gettempdir()
if chdir:
- os.chdir(chdir)
- rc, out_pip, err_pip = module.run_command(cmd, path_prefix=path_prefix)
+ this_dir = os.path.join(this_dir, chdir)
+
+ rc, out_pip, err_pip = module.run_command(cmd, path_prefix=path_prefix, cwd=this_dir)
out += out_pip
err += err_pip
if rc == 1 and state == 'absent' and 'not installed' in out_pip:
diff --git a/library/packaging/redhat_subscription b/library/packaging/redhat_subscription
index e363aa0946a..bb5d655a52f 100644
--- a/library/packaging/redhat_subscription
+++ b/library/packaging/redhat_subscription
@@ -75,39 +75,13 @@ EXAMPLES = '''
import os
import re
import types
-import subprocess
import ConfigParser
import shlex
-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):
+class RegistrationBase(object):
+ def __init__(self, module, username=None, password=None):
+ self.module = module
self.username = username
self.password = password
@@ -147,9 +121,10 @@ class RegistrationBase (object):
class Rhsm(RegistrationBase):
- def __init__(self, username=None, password=None):
- RegistrationBase.__init__(self, username, password)
+ 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'):
'''
@@ -199,8 +174,8 @@ class Rhsm(RegistrationBase):
for k,v in kwargs.items():
if re.search(r'^(system|rhsm)_', k):
args.append('--%s=%s' % (k.replace('_','.'), v))
-
- run_command(args)
+
+ self.module.run_command(args, check_rc=True)
@property
def is_registered(self):
@@ -216,13 +191,11 @@ class Rhsm(RegistrationBase):
os.path.isfile('/etc/pki/consumer/key.pem')
args = ['subscription-manager', 'identity']
- try:
- (stdout, stderr, retcode) = run_command(args)
- except CommandException, e:
- return False
- else:
- # Display some debug output
+ 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):
'''
@@ -244,7 +217,7 @@ class Rhsm(RegistrationBase):
args.extend(['--password', password])
# Do the needful...
- run_command(args)
+ rc, stderr, stdout = self.module.run_command(args, check_rc=True)
def unsubscribe(self):
'''
@@ -253,7 +226,7 @@ class Rhsm(RegistrationBase):
* Exception - if error occurs while running command
'''
args = ['subscription-manager', 'unsubscribe', '--all']
- run_command(args)
+ rc, stderr, stdout = self.module.run_command(args, check_rc=True)
def unregister(self):
'''
@@ -262,7 +235,7 @@ class Rhsm(RegistrationBase):
* Exception - if error occurs while running command
'''
args = ['subscription-manager', 'unregister']
- run_command(args)
+ rc, stderr, stdout = self.module.run_command(args, check_rc=True)
def subscribe(self, regexp):
'''
@@ -273,7 +246,7 @@ class Rhsm(RegistrationBase):
'''
# Available pools ready for subscription
- available_pools = RhsmPools()
+ available_pools = RhsmPools(self.module)
for pool in available_pools.filter(regexp):
pool.subscribe()
@@ -284,7 +257,8 @@ class RhsmPool(object):
Convenience class for housing subscription information
'''
- def __init__(self, **kwargs):
+ def __init__(self, module, **kwargs):
+ self.module = module
for k,v in kwargs.items():
setattr(self, k, v)
@@ -292,15 +266,20 @@ class RhsmPool(object):
return str(self.__getattribute__('_name'))
def subscribe(self):
- (stdout, stderr, retcode) = run_command("subscription-manager subscribe --pool %s" % self.PoolId)
- return True
+ 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):
+ def __init__(self, module):
+ self.module = module
self.products = self._load_product_list()
def __iter__(self):
@@ -310,7 +289,8 @@ class RhsmPools(object):
"""
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 = []
for line in stdout.split('\n'):
@@ -326,7 +306,7 @@ class RhsmPools(object):
value = value.strip()
if key in ['ProductName', 'SubscriptionName']:
# Remember the name for later processing
- products.append(RhsmPool(_name=value, key=value))
+ products.append(RhsmPool(self.module, _name=value, key=value))
elif products:
# Associate value with most recently recorded product
products[-1].__setattr__(key, value)
@@ -348,7 +328,7 @@ class RhsmPools(object):
def main():
# Load RHSM configuration from file
- rhn = Rhsm()
+ rhn = Rhsm(AnsibleModule())
module = AnsibleModule(
argument_spec = dict(
@@ -364,6 +344,7 @@ def main():
)
)
+ rhn.module = module
state = module.params['state']
username = module.params['username']
password = module.params['password']
diff --git a/library/packaging/rhn_register b/library/packaging/rhn_register
index 5e8c3718f98..28d91a6a027 100644
--- a/library/packaging/rhn_register
+++ b/library/packaging/rhn_register
@@ -72,12 +72,7 @@ EXAMPLES = '''
'''
import sys
-import os
-import re
import types
-import subprocess
-import ConfigParser
-import shlex
import xmlrpclib
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)
-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):
- def __init__(self, username=None, password=None):
+ def __init__(self, module, username=None, password=None):
RegistrationBase.__init__(self, username, password)
self.config = self.load_config()
@@ -271,7 +200,7 @@ class Rhn(RegistrationBase):
register_cmd += " --activationkey '%s'" % activationkey
# FIXME - support --profilename
# FIXME - support --systemorgid
- run_command(register_cmd)
+ rc, stdout, stderr = self.module.run_command(register_command, check_rc=True)
def api(self, method, *args):
'''
@@ -309,14 +238,14 @@ class Rhn(RegistrationBase):
Subscribe to requested yum repositories using 'rhn-channel' command
'''
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
for wanted_channel in channels:
# 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
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():
@@ -379,4 +308,6 @@ def main():
# import module snippets
from ansible.module_utils.basic import *
+from ansible.module_utils.redhat import *
+
main()
diff --git a/library/packaging/urpmi b/library/packaging/urpmi
index b001ed94dee..72dfef02011 100644
--- a/library/packaging/urpmi
+++ b/library/packaging/urpmi
@@ -91,7 +91,8 @@ def query_package(module, name):
# rpm -q returns 0 if the package is 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:
return True
else:
@@ -103,13 +104,14 @@ def query_package_provides(module, name):
# rpm -q returns 0 if the package is 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
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:
module.fail_json(msg="could not update package db")
@@ -123,7 +125,8 @@ def remove_packages(module, packages):
if not query_package(module, package):
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:
module.fail_json(msg="failed to remove %s" % (package))
diff --git a/library/source_control/bzr b/library/source_control/bzr
index bc2dfc3089f..5217e469900 100644
--- a/library/source_control/bzr
+++ b/library/source_control/bzr
@@ -75,16 +75,17 @@ class Bzr(object):
self.version = version
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(
- [self.bzr_path] + args_list, **kwargs)
+ [self.bzr_path] + args_list, cwd=cwd, **kwargs)
return (rc, out, err)
def get_version(self):
'''samples the version of the bzr branch'''
- os.chdir(self.dest)
+
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
def clone(self):
@@ -94,17 +95,18 @@ class Bzr(object):
os.makedirs(dest_dirname)
except:
pass
- os.chdir(dest_dirname)
if self.version.lower() != 'head':
args_list = ["branch", "-r", self.version, self.parent, self.dest]
else:
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):
- os.chdir(self.dest)
+
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)
return len(lines) > 0
@@ -114,30 +116,27 @@ class Bzr(object):
Discards any changes to tracked files in the working
tree since that commit.
'''
- os.chdir(self.dest)
if not force and self.has_local_mods():
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):
'''updates branch from remote sources'''
- os.chdir(self.dest)
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:
- (rc, out, err) = self._command(["pull"])
+ (rc, out, err) = self._command(["pull"], cwd=self.dest)
if rc != 0:
self.module.fail_json(msg="Failed to pull")
return (rc, out, err)
def switch_version(self):
'''once pulled, switch to a particular revno or revid'''
- os.chdir(self.dest)
if self.version.lower() != 'head':
args_list = ["revert", "-r", self.version]
else:
args_list = ["revert"]
- return self._command(args_list, check_rc=True)
+ return self._command(args_list, check_rc=True, cwd=self.dest)
# ===========================================
diff --git a/library/source_control/git b/library/source_control/git
index ca876c666b5..4f885c94001 100644
--- a/library/source_control/git
+++ b/library/source_control/git
@@ -181,11 +181,12 @@ def set_git_ssh(ssh_wrapper, key_file, ssh_opts):
if 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 '''
- os.chdir(dest)
+
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
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)
except:
pass
- os.chdir(dest_dirname)
cmd = [ git_path, 'clone' ]
if bare:
cmd.append('--bare')
@@ -209,19 +209,19 @@ def clone(git_path, module, repo, dest, remote, depth, version, bare, reference)
if reference:
cmd.extend([ '--reference', str(reference) ])
cmd.extend([ repo, dest ])
- module.run_command(cmd, check_rc=True)
+ module.run_command(cmd, check_rc=True, cwd=dest_dirname)
if bare:
- os.chdir(dest)
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:
return False
- os.chdir(dest)
- cmd = "%s status -s" % (git_path,)
- lines = os.popen(cmd).read().splitlines()
- lines = filter(lambda c: not re.search('^\\?\\?.*$', c), lines)
+
+ cmd = "%s status -s" % (git_path)
+ rc, stdout, stderr = module.run_command(cmd, cwd=dest)
+ lines = stdout.splitlines()
+
return len(lines) > 0
def reset(git_path, module, dest):
@@ -230,16 +230,16 @@ def reset(git_path, module, dest):
Discards any changes to tracked files in working
tree since that commit.
'''
- os.chdir(dest)
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):
cloning = False
+ cwd = None
if remote == module.params['repo']:
cloning = True
else:
- os.chdir(dest)
+ cwd = dest
if version == 'HEAD':
if cloning:
# 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
# cannot check for a specific sha1 on remote
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:
module.fail_json(msg="Could not determine remote revision for %s" % version)
rev = out.split()[0]
@@ -270,10 +270,9 @@ def is_remote_tag(git_path, module, dest, remote, version):
return False
def get_branches(git_path, module, dest):
- os.chdir(dest)
branches = []
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:
module.fail_json(msg="Could not determine branch data - received %s" % out)
for line in out.split('\n'):
@@ -281,10 +280,9 @@ def get_branches(git_path, module, dest):
return branches
def get_tags(git_path, module, dest):
- os.chdir(dest)
tags = []
cmd = '%s tag' % (git_path,)
- (rc, out, err) = module.run_command(cmd)
+ (rc, out, err) = module.run_command(cmd, cwd=dest)
if rc != 0:
module.fail_json(msg="Could not determine tag data - received %s" % out)
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):
''' updates repo from remote sources '''
- os.chdir(dest)
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:
- (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:
module.fail_json(msg="Failed to download remote objects and refs")
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:
- (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:
module.fail_json(msg="Failed to download remote objects and refs")
(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):
''' init and update any submodules '''
- os.chdir(dest)
# skip submodule commands if .gitmodules is not present
if not os.path.exists(os.path.join(dest, '.gitmodules')):
return (0, '', '')
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' ]
- (rc, out, err) = module.run_command(cmd)
+ (rc, out, err) = module.run_command(cmd, cwd=dest)
if rc != 0:
module.fail_json(msg="Failed to init/update submodules")
return (rc, out, err)
def switch_version(git_path, module, dest, remote, version):
''' once pulled, switch to a particular SHA, tag, or branch '''
- os.chdir(dest)
cmd = ''
if version != 'HEAD':
if is_remote_branch(git_path, module, dest, remote, version):
if not is_local_branch(git_path, module, dest, version):
cmd = "%s checkout --track -b %s %s/%s" % (git_path, version, remote, version)
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:
module.fail_json(msg="Failed to checkout branch %s" % 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)
else:
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:
module.fail_json(msg="Failed to checkout branch %s" % branch)
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 version != 'HEAD':
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
# this does no checking that the repo is the actual repo
# requested.
- before = get_version(git_path, dest)
+ before = get_version(module, git_path, dest)
module.exit_json(changed=False, before=before, after=before)
else:
# else do a pull
- local_mods = has_local_mods(git_path, dest, bare)
- before = get_version(git_path, dest)
+ local_mods = has_local_mods(module, git_path, dest, bare)
+ before = get_version(module, git_path, dest)
if local_mods:
# failure should happen regardless of check mode
if not force:
@@ -519,7 +514,7 @@ def main():
switch_version(git_path, module, dest, remote, version)
# determine if we changed anything
- after = get_version(git_path, dest)
+ after = get_version(module, git_path, dest)
changed = False
if before != after or local_mods:
diff --git a/library/system/service b/library/system/service
index 2e26a47b636..5180a14d82b 100644
--- a/library/system/service
+++ b/library/system/service
@@ -207,7 +207,9 @@ class Service(object):
os._exit(0)
# 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 = ""
stderr = ""
fds = [p.stdout, p.stderr]
diff --git a/library/system/setup b/library/system/setup
index 941a5dcd31a..1c156f6ce34 100644
--- a/library/system/setup
+++ b/library/system/setup
@@ -29,7 +29,6 @@ import socket
import struct
import datetime
import getpass
-import subprocess
import ConfigParser
import StringIO
@@ -1430,7 +1429,8 @@ class LinuxNetwork(Network):
"""
platform = 'Linux'
- def __init__(self):
+ def __init__(self, module):
+ self.module = module
Network.__init__(self)
def populate(self):
@@ -1616,12 +1616,15 @@ class LinuxNetwork(Network):
ips['all_ipv6_addresses'].append(address)
ip_path = module.get_bin_path("ip")
- primary_data = subprocess.Popen(
- [ip_path, 'addr', 'show', 'primary', device],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
- secondary_data = subprocess.Popen(
- [ip_path, 'addr', 'show', 'secondary', device],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
+
+ args = [ip_path, 'addr', 'show', 'primary', device]
+ rc, stdout, stderr = self.module.run_command(args)
+ primary_data = stdout
+
+ 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(secondary_data, secondary=True)
@@ -2281,11 +2284,11 @@ def get_file_content(path, default=None):
data = default
return data
-def ansible_facts():
+def ansible_facts(module):
facts = {}
facts.update(Facts().populate())
facts.update(Hardware().populate())
- facts.update(Network().populate())
+ facts.update(Network(module).populate())
facts.update(Virtual().populate())
return facts
@@ -2294,7 +2297,7 @@ def ansible_facts():
def run_setup(module):
setup_options = {}
- facts = ansible_facts()
+ facts = ansible_facts(module)
for (k, v) in facts.items():
setup_options["ansible_%s" % k.replace('-', '_')] = v
diff --git a/library/web_infrastructure/django_manage b/library/web_infrastructure/django_manage
index 68eb92c1bfe..b02a9398f52 100644
--- a/library/web_infrastructure/django_manage
+++ b/library/web_infrastructure/django_manage
@@ -232,7 +232,6 @@ def main():
_ensure_virtualenv(module)
- os.chdir(app_path)
cmd = "python manage.py %s" % (command, )
if command in noinput_commands:
@@ -251,7 +250,7 @@ def main():
if 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 command == 'createcachetable' and 'table' in err and 'already exists' in err:
out = 'Already exists.'