diff --git a/TODO.md b/TODO.md index 9dcc533c01..07e7d79eae 100644 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,9 @@ TODO list and plans =================== - * make remote user settable versus assuming remote login is named root * modules for users, groups, and files, using puppet style ensure mechanics - * ansible-inventory -- gathering fact/hw info, storing in git, adding RSS - * ansible-slurp ------ recursively rsync file trees for each host + * ansible-inventory - gathering fact/hw info, storing in git, adding RSS + * ansible-slurp - recursively rsync file trees for each host * very simple option constructing/parsing for modules * Dead-simple declarative configuration management engine using a runbook style recipe file, written in JSON or YAML diff --git a/bin/ansible b/bin/ansible index 6425dcd714..bb8cce7295 100755 --- a/bin/ansible +++ b/bin/ansible @@ -32,6 +32,7 @@ DEFAULT_MODULE_NAME = 'ping' DEFAULT_PATTERN = '*' DEFAULT_FORKS = 3 DEFAULT_MODULE_ARGS = '' +DEFAULT_REMOTE_USER = 'root' class Cli(object): @@ -44,7 +45,7 @@ class Cli(object): help="path to hosts list", default=DEFAULT_HOST_LIST) parser.add_option("-L", "--library", dest="module_path", help="path to module library", default=DEFAULT_MODULE_PATH) - parser.add_option("-F", "--forks", dest="forks", + parser.add_option("-f", "--forks", dest="forks", help="level of parallelism", default=DEFAULT_FORKS) parser.add_option("-n", "--name", dest="module_name", help="module name to execute", default=DEFAULT_MODULE_NAME) @@ -52,6 +53,8 @@ class Cli(object): help="module arguments", default=DEFAULT_MODULE_ARGS) parser.add_option("-p", "--pattern", dest="pattern", help="hostname pattern", default=DEFAULT_PATTERN) + parser.add_option("-u", "--remote-user", dest="remote_user", + help="remote username", default=DEFAULT_REMOTE_USER) options, args = parser.parse_args() @@ -62,6 +65,7 @@ class Cli(object): module_name=options.module_name, module_path=options.module_path, module_args=options.module_args.split(' '), + remote_user=options.remote_user, host_list=options.host_list, forks=options.forks, pattern=options.pattern, diff --git a/lib/ansible/__init__.py b/lib/ansible/__init__.py index 4af0ba405a..77012dda9a 100755 --- a/lib/ansible/__init__.py +++ b/lib/ansible/__init__.py @@ -36,6 +36,7 @@ DEFAULT_PATTERN = '*' DEFAULT_FORKS = 3 DEFAULT_MODULE_ARGS = '' DEFAULT_TIMEOUT = 60 +DEFAULT_REMOTE_USER = 'root' class Pooler(object): @@ -66,6 +67,7 @@ class Runner(object): forks=DEFAULT_FORKS, timeout=DEFAULT_TIMEOUT, pattern=DEFAULT_PATTERN, + remote_user=DEFAULT_REMOTE_USER, verbose=False): @@ -81,6 +83,7 @@ class Runner(object): self.module_args = module_args self.timeout = timeout self.verbose = verbose + self.remote_user = remote_user def _parse_hosts(self, host_list): ''' parse the host inventory file if not sent as an array ''' @@ -103,23 +106,19 @@ class Runner(object): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: - ssh.connect(host, username='root', + ssh.connect(host, username=self.remote_user, allow_agent=True, look_for_keys=True) - return ssh + return [ True, ssh ] except: - # TODO -- just convert traceback to string - # and return a seperate hash of failed hosts - if self.verbose: - traceback.print_exc() - return None + return [ False, traceback.format_exc() ] def _executor(self, host): ''' callback executed in parallel for each host ''' # TODO: try/catch returning none - conn = self._connect(host) - if not conn: - return [ host, None ] + ok, conn = self._connect(host) + if not ok: + return [ host, False, conn ] if self.module_name != "copy": # transfer a module, set it executable, and run it @@ -127,15 +126,14 @@ class Runner(object): self._exec_command(conn, "chmod +x %s" % outpath) cmd = self._command(outpath) result = self._exec_command(conn, cmd) - result = json.loads(result) + return [ host, True, json.loads(result) ] else: # SFTP file copy module is not really a module ftp = conn.open_sftp() ftp.put(self.module_args[0], self.module_args[1]) ftp.close() - return [ host, 1 ] + return [ host, True, 1 ] - return [ host, result ] def _command(self, outpath): ''' form up a command string ''' @@ -168,8 +166,17 @@ class Runner(object): def executor(x): return self._executor(x) results = Pooler.parmap(executor, hosts) - by_host = dict(results) - return by_host + results2 = { + "successful" : {}, + "failed" : {} + } + for x in results: + (host, is_ok, result) = x + if not is_ok: + results2["failed"][host] = result + else: + results2["successful"][host] = result + return results2 if __name__ == '__main__':