From 4c62e495eb9a9c8f230f3cfffcfef0e7289f0e20 Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Fri, 17 Aug 2012 17:10:08 -0700 Subject: [PATCH 1/5] Add method get_bin_path to module_common.py This is meant to assist all the modules that look for the full path of an executable. If it is found and is X_OK, returns the full path. Otherwise, it returns None. --- lib/ansible/module_common.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/ansible/module_common.py b/lib/ansible/module_common.py index 29db24dd984..c9179af6db0 100644 --- a/lib/ansible/module_common.py +++ b/lib/ansible/module_common.py @@ -202,6 +202,25 @@ class AnsibleModule(object): log_args = re.sub(r'login_password=.+ (.*)', r"login_password=NOT_LOGGING_PASSWORD \1", log_args) syslog.syslog(syslog.LOG_NOTICE, 'Invoked with %s' % log_args) + def get_bin_path(self, arg): + ''' + find system executable in PATH. + if found return full path; otherwise return None + ''' + sbin_paths = ['/sbin', '/usr/sbin', '/usr/local/sbin'] + paths = os.environ.get('PATH').split(':') + bin_path = None + # mangle PATH to include /sbin dirs + for p in sbin_paths: + if p not in paths and os.path.exists(p): + paths.append(p) + for d in paths: + path = '%s/%s' % (d, arg) + if os.path.exists(path) and os.access(path, os.X_OK): + bin_path = path + break + return bin_path + def boolean(self, arg): ''' return a bool for the arg ''' if arg is None or type(arg) == bool: From bdb39058ae4b0f21d64606f253879df8fbc79215 Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Tue, 21 Aug 2012 07:36:38 -0700 Subject: [PATCH 2/5] Migrate apt_repository, group, and supervisorctl to use module.get_bin_path --- library/apt_repository | 17 +++++------------ library/group | 9 ++++----- library/supervisorctl | 4 +++- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/library/apt_repository b/library/apt_repository index 8724f7c301e..e5d8d46c5f2 100755 --- a/library/apt_repository +++ b/library/apt_repository @@ -26,17 +26,7 @@ import platform APT = "/usr/bin/apt-get" - - -def _find_binary(module): - binaries = ['/usr/bin/add-apt-repository'] - - for e in binaries: - if os.path.exists(e): - return e - - module.fail_json(msg='Unabled to find any of the following executables ' - '%s' % binaries) +ADD_APT_REPO = 'add-apt-repository' def _run(cmd): # returns (rc, stdout, stderr) from shell command @@ -56,7 +46,10 @@ def main(): module = AnsibleModule(argument_spec=arg_spec) - add_apt_repository = _find_binary(module) + add_apt_repository = module.get_bin_path(ADD_APT_REPO) + if add_apt_repository is None: + module.fail_json(msg='Unabled to find any of the following executables ' + '%s' % binaries) repo = module.params['repo'] state = module.params['state'] diff --git a/library/group b/library/group index 23e8703c496..d4c160ebb5d 100755 --- a/library/group +++ b/library/group @@ -21,12 +21,11 @@ import grp def get_bin_path(module, arg): - if os.path.exists('/usr/sbin/%s' % arg): - return '/usr/sbin/%s' % arg - elif os.path.exists('/sbin/%s' % arg): - return '/sbin/%s' % arg - else: + bin = module.get_bin_path(arg) + if bin is None: module.fail_json(msg="Cannot find %s" % arg) + else: + return bin def group_del(module, group): cmd = [get_bin_path(module, 'groupdel'), group] diff --git a/library/supervisorctl b/library/supervisorctl index 541caf1fade..e0671a5869b 100755 --- a/library/supervisorctl +++ b/library/supervisorctl @@ -52,7 +52,9 @@ def main(): name = module.params['name'] state = module.params['state'] - SUPERVISORCTL = _find_supervisorctl() + SUPERVISORCTL = module.get_bin_path('supervisorctl') + if SUPERVISORCTL is None: + module.fail_json(msg='supervisorctl is not installed') if SUPERVISORCTL is None: module.fail_json(msg='supervisorctl is not installed') From 4e7b67a45a3f46d0e770a547d51bc65f850d8096 Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Wed, 22 Aug 2012 23:28:14 -0700 Subject: [PATCH 3/5] Add option to pass list of dirs to get_bin_path in module_common.py The optional list is prepended to PATH. Fix get_bin_path() to use os.path.join(). --- lib/ansible/module_common.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/ansible/module_common.py b/lib/ansible/module_common.py index c9179af6db0..aee5128c606 100644 --- a/lib/ansible/module_common.py +++ b/lib/ansible/module_common.py @@ -202,20 +202,24 @@ class AnsibleModule(object): log_args = re.sub(r'login_password=.+ (.*)', r"login_password=NOT_LOGGING_PASSWORD \1", log_args) syslog.syslog(syslog.LOG_NOTICE, 'Invoked with %s' % log_args) - def get_bin_path(self, arg): + def get_bin_path(self, arg, opt_dirs=[]): ''' find system executable in PATH. if found return full path; otherwise return None ''' sbin_paths = ['/sbin', '/usr/sbin', '/usr/local/sbin'] - paths = os.environ.get('PATH').split(':') + paths = [] + for d in opt_dirs: + if d is not None and os.path.exists(d): + paths.append(d) + paths += os.environ.get('PATH').split(':') bin_path = None # mangle PATH to include /sbin dirs for p in sbin_paths: if p not in paths and os.path.exists(p): paths.append(p) for d in paths: - path = '%s/%s' % (d, arg) + path = os.path.join(d, arg) if os.path.exists(path) and os.access(path, os.X_OK): bin_path = path break From e5a635672c3c7e9b2f27d1ca04ddd006d61ba3ed Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Wed, 29 Aug 2012 20:26:22 -0700 Subject: [PATCH 4/5] Migrate remaining modules to use get_bin_path in module_common.py * Migraed easy_install, pip, service, setup, and user. * Updated fail_json message in apt_repository * Fixed easy_install to not hardcode location of virtualenv in /usr/local/bin/. * Made handling of virtualenv more consistent between easy_install and pip. --- library/apt_repository | 3 +-- library/easy_install | 22 ++++++++-------------- library/pip | 34 ++++++---------------------------- library/service | 5 +---- library/setup | 14 ++++---------- library/user | 9 ++++----- 6 files changed, 24 insertions(+), 63 deletions(-) diff --git a/library/apt_repository b/library/apt_repository index e5d8d46c5f2..60539a86c6d 100755 --- a/library/apt_repository +++ b/library/apt_repository @@ -48,8 +48,7 @@ def main(): add_apt_repository = module.get_bin_path(ADD_APT_REPO) if add_apt_repository is None: - module.fail_json(msg='Unabled to find any of the following executables ' - '%s' % binaries) + module.fail_json(msg='Unable to find executable %s' % ADD_APT_REPO) repo = module.params['repo'] state = module.params['state'] diff --git a/library/easy_install b/library/easy_install index 47d3f2c8237..eeb010d1fba 100755 --- a/library/easy_install +++ b/library/easy_install @@ -19,18 +19,6 @@ # along with Ansible. If not, see . # -def _find_easy_install(env): - if env: - return os.path.join(env, 'bin', 'easy_install') - - paths = ['/usr/local/bin', '/usr/bin'] - - for p in paths: - e = p + '/easy_install' - if os.path.exists(e): - return e - - def _ensure_virtualenv(env, virtualenv): if os.path.exists(os.path.join(env, 'bin', 'activate')): return 0, '', '' @@ -62,14 +50,20 @@ def main(): name = module.params['name'] env = module.params['virtualenv'] - easy_install = _find_easy_install(env) + easy_install = module.get_bin_path('easy_install', ['%s/bin' % env]) + if easy_install is None: + module.fail_json(msg='easy_install is not installed') rc = 0 err = '' out = '' if env: - rc_venv, out_venv, err_venv = _ensure_virtualenv(env, '/usr/local/bin/virtualenv') + virtualenv = module.get_bin_path('virtualenv') + if virtualenv is None: + module.fail_json(msg='virtualenv is not installed') + + rc_venv, out_venv, err_venv = _ensure_virtualenv(env, virtualenv) rc += rc_venv out += out_venv diff --git a/library/pip b/library/pip index a2a337457ca..5dbbea561d2 100755 --- a/library/pip +++ b/library/pip @@ -27,32 +27,6 @@ def _get_full_name(name, version=None): resp = name + '==' + version return resp - -def _find_pip(module, env): - paths = ['/usr/local/bin', '/usr/bin'] - - if env: - paths = [os.path.join(env, 'bin')] + paths - - for p in paths: - pe = p + '/pip' - if os.path.exists(pe): - return pe - - module.fail_json(msg='pip is not installed') - - -def _find_virtualenv(module): - paths = ['/usr/local/bin', '/usr/bin'] - - for p in paths: - ve = p + '/virtualenv' - if os.path.exists(ve): - return ve - - module.fail_json(msg='virtualenv is not installed') - - def _ensure_virtualenv(module, env, virtualenv): if os.path.exists(os.path.join(env, 'bin', 'activate')): return 0, '', '' @@ -103,7 +77,9 @@ def main(): env = module.params['virtualenv'] if env: - virtualenv = _find_virtualenv(module) + virtualenv = module.get_bin_path('virtualenv') + if virtualenv is None: + module.fail_json(msg='virtualenv is not installed') rc_venv, out_venv, err_venv = _ensure_virtualenv(module, env, virtualenv) @@ -111,7 +87,9 @@ def main(): out += out_venv err += err_venv - pip = _find_pip(module, env) + pip = module.get_bin_path('pip', ['%s/bin' % env]) + if pip is None: + module.fail_json(msg='pip is not installed') state = module.params['state'] name = module.params['name'] diff --git a/library/service b/library/service index e884f902ed5..0d918d49035 100755 --- a/library/service +++ b/library/service @@ -39,10 +39,7 @@ def _find_binaries(m): location[binary] = None for binary in binaries: - for path in paths: - if os.path.exists(path + '/' + binary): - location[binary] = path + '/' + binary - break + location[binary] = m.get_bin_path(binary) if location.get('systemctl', None): CHKCONFIG = location['systemctl'] diff --git a/library/setup b/library/setup index 4ed85620cd7..005919157b1 100755 --- a/library/setup +++ b/library/setup @@ -436,7 +436,9 @@ class LinuxNetwork(Network): Network.__init__(self) def populate(self): - ip_path = self.get_ip_path() + ip_path = module.get_bin_path('ip') + if ip_path is None: + return self.facts default_ipv4, default_ipv6 = self.get_default_interfaces(ip_path) interfaces, ips = self.get_interfaces_info(ip_path, default_ipv4, default_ipv6) self.facts['interfaces'] = interfaces.keys() @@ -448,15 +450,6 @@ class LinuxNetwork(Network): self.facts['all_ipv6_addresses'] = ips['all_ipv6_addresses'] return self.facts - def get_ip_path(self): - paths = ['/sbin/ip', '/usr/sbin/ip'] - ip_path = None - for path in paths: - if os.path.exists(path): - ip_path = path - break - return ip_path - def get_default_interfaces(self, ip_path): # Use the commands: # ip -4 route get 8.8.8.8 -> Google public DNS @@ -730,6 +723,7 @@ def run_setup(module): return setup_result def main(): + global module module = AnsibleModule( argument_spec = dict() ) diff --git a/library/user b/library/user index c90037efe6b..a932978da86 100755 --- a/library/user +++ b/library/user @@ -36,12 +36,11 @@ if os.path.exists('/etc/master.passwd'): # That is, this won't work on FreeBSD. def get_bin_path(module, arg): - if os.path.exists('/usr/sbin/%s' % arg): - return '/usr/sbin/%s' % arg - elif os.path.exists('/sbin/%s' % arg): - return '/sbin/%s' % arg - else: + bin = module.get_bin_path(arg) + if bin is None: module.fail_json(msg="Cannot find %s" % arg) + else: + return bin def user_del(module, user, **kwargs): cmd = [get_bin_path(module, 'userdel')] From 6742e9c3f4eee72653a6b6d9f7f41fa372daec47 Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Thu, 30 Aug 2012 10:31:23 -0700 Subject: [PATCH 5/5] Add option required=(True|False) to get_bin_path and update modules Added required as optional argument to get_bin_path(). It defaults to false. Updated following modules to use required=True when calling get_bin_path(): apt_repository, easy_install, group, pip, supervisorctl, and user. Also removed _find_supervisorctl() from supervisorctl module and updated _is_running() to not need it. --- lib/ansible/module_common.py | 7 ++++++- library/apt_repository | 4 +--- library/easy_install | 6 ++---- library/group | 13 +++---------- library/pip | 8 ++------ library/supervisorctl | 19 ++++--------------- library/user | 13 +++---------- 7 files changed, 21 insertions(+), 49 deletions(-) diff --git a/lib/ansible/module_common.py b/lib/ansible/module_common.py index aee5128c606..c2376564415 100644 --- a/lib/ansible/module_common.py +++ b/lib/ansible/module_common.py @@ -202,9 +202,12 @@ class AnsibleModule(object): log_args = re.sub(r'login_password=.+ (.*)', r"login_password=NOT_LOGGING_PASSWORD \1", log_args) syslog.syslog(syslog.LOG_NOTICE, 'Invoked with %s' % log_args) - def get_bin_path(self, arg, opt_dirs=[]): + def get_bin_path(self, arg, required=False, opt_dirs=[]): ''' find system executable in PATH. + Optional arguments: + - required: if executable is not found and required is true, fail_json + - opt_dirs: optional list of directories to search in addition to PATH if found return full path; otherwise return None ''' sbin_paths = ['/sbin', '/usr/sbin', '/usr/local/sbin'] @@ -223,6 +226,8 @@ class AnsibleModule(object): if os.path.exists(path) and os.access(path, os.X_OK): bin_path = path break + if required and bin_path is None: + self.fail_json(msg='Failed to find required executable %s' % arg) return bin_path def boolean(self, arg): diff --git a/library/apt_repository b/library/apt_repository index 60539a86c6d..67c13937002 100755 --- a/library/apt_repository +++ b/library/apt_repository @@ -46,9 +46,7 @@ def main(): module = AnsibleModule(argument_spec=arg_spec) - add_apt_repository = module.get_bin_path(ADD_APT_REPO) - if add_apt_repository is None: - module.fail_json(msg='Unable to find executable %s' % ADD_APT_REPO) + add_apt_repository = module.get_bin_path(ADD_APT_REPO, True) repo = module.params['repo'] state = module.params['state'] diff --git a/library/easy_install b/library/easy_install index eeb010d1fba..cacd5203ede 100755 --- a/library/easy_install +++ b/library/easy_install @@ -50,16 +50,14 @@ def main(): name = module.params['name'] env = module.params['virtualenv'] - easy_install = module.get_bin_path('easy_install', ['%s/bin' % env]) - if easy_install is None: - module.fail_json(msg='easy_install is not installed') + easy_install = module.get_bin_path('easy_install', True, ['%s/bin' % env]) rc = 0 err = '' out = '' if env: - virtualenv = module.get_bin_path('virtualenv') + virtualenv = module.get_bin_path('virtualenv', True) if virtualenv is None: module.fail_json(msg='virtualenv is not installed') diff --git a/library/group b/library/group index d4c160ebb5d..6df242d63a2 100755 --- a/library/group +++ b/library/group @@ -20,22 +20,15 @@ import grp -def get_bin_path(module, arg): - bin = module.get_bin_path(arg) - if bin is None: - module.fail_json(msg="Cannot find %s" % arg) - else: - return bin - def group_del(module, group): - cmd = [get_bin_path(module, 'groupdel'), group] + cmd = [module.get_bin_path('groupdel', True), group] p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = p.communicate() rc = p.returncode return (rc, out, err) def group_add(module, group, **kwargs): - cmd = [get_bin_path(module, 'groupadd')] + cmd = [module.get_bin_path('groupadd', True)] for key in kwargs: if key == 'gid' and kwargs[key] is not None: cmd.append('-g') @@ -49,7 +42,7 @@ def group_add(module, group, **kwargs): return (rc, out, err) def group_mod(module, group, **kwargs): - cmd = [get_bin_path(module, 'groupmod')] + cmd = [module.get_bin_path('groupmod', True)] info = group_info(group) for key in kwargs: if key == 'gid': diff --git a/library/pip b/library/pip index 5dbbea561d2..7ff047082fa 100755 --- a/library/pip +++ b/library/pip @@ -77,9 +77,7 @@ def main(): env = module.params['virtualenv'] if env: - virtualenv = module.get_bin_path('virtualenv') - if virtualenv is None: - module.fail_json(msg='virtualenv is not installed') + virtualenv = module.get_bin_path('virtualenv', True) rc_venv, out_venv, err_venv = _ensure_virtualenv(module, env, virtualenv) @@ -87,9 +85,7 @@ def main(): out += out_venv err += err_venv - pip = module.get_bin_path('pip', ['%s/bin' % env]) - if pip is None: - module.fail_json(msg='pip is not installed') + pip = module.get_bin_path('pip', True, ['%s/bin' % env]) state = module.params['state'] name = module.params['name'] diff --git a/library/supervisorctl b/library/supervisorctl index e0671a5869b..97502ff687f 100755 --- a/library/supervisorctl +++ b/library/supervisorctl @@ -19,17 +19,8 @@ # along with Ansible. If not, see . # -def _find_supervisorctl(): - paths = ['/usr/local/bin', '/usr/bin'] - - for p in paths: - e = p + '/supervisorctl' - if os.path.exists(e): - return e - - -def _is_running(name): - rc, out, err = _run('%s status %s' % (_find_supervisorctl(), name)) +def _is_running(name, supervisorctl): + rc, out, err = _run('%s status %s' % (supervisorctl, name)) return 'RUNNING' in out @@ -52,14 +43,12 @@ def main(): name = module.params['name'] state = module.params['state'] - SUPERVISORCTL = module.get_bin_path('supervisorctl') - if SUPERVISORCTL is None: - module.fail_json(msg='supervisorctl is not installed') + SUPERVISORCTL = module.get_bin_path('supervisorctl', True) if SUPERVISORCTL is None: module.fail_json(msg='supervisorctl is not installed') - running = _is_running(name) + running = _is_running(name, SUPERVISORCTL) if running and state == 'started': module.exit_json(changed=False, name=name, state=state) diff --git a/library/user b/library/user index a932978da86..d36c0cffce5 100755 --- a/library/user +++ b/library/user @@ -35,15 +35,8 @@ if os.path.exists('/etc/master.passwd'): # invoke adduser in lieu of useradd, nor pw in lieu of usermod. # That is, this won't work on FreeBSD. -def get_bin_path(module, arg): - bin = module.get_bin_path(arg) - if bin is None: - module.fail_json(msg="Cannot find %s" % arg) - else: - return bin - def user_del(module, user, **kwargs): - cmd = [get_bin_path(module, 'userdel')] + cmd = [module.get_bin_path('userdel', True)] for key in kwargs: if key == 'force' and kwargs[key] == 'yes': cmd.append('-f') @@ -56,7 +49,7 @@ def user_del(module, user, **kwargs): return (rc, out, err) def user_add(module, user, **kwargs): - cmd = [get_bin_path(module, 'useradd')] + cmd = [module.get_bin_path('useradd', True)] for key in kwargs: if key == 'uid' and kwargs[key] is not None: cmd.append('-u') @@ -103,7 +96,7 @@ Without spwd, we would have to resort to reading /etc/shadow to get the encrypted string. For now, punt on idempotent password changes. """ def user_mod(module, user, **kwargs): - cmd = [get_bin_path(module, 'usermod')] + cmd = [module.get_bin_path('usermod', True)] info = user_info(user) for key in kwargs: if key == 'uid':