def put_file(self, in_path, out_path): ''' transfer a file from local to remote ''' vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise AnsibleFileNotFound("file or module does not exist: %s" % in_path) fd = file(in_path, 'rb') fstat = os.stat(in_path) try: vvv("PUT file is %d bytes" % fstat.st_size) last = False while fd.tell() <= fstat.st_size and not last: vvvv("file position currently %ld, file size is %ld" % (fd.tell(), fstat.st_size)) data = fd.read(CHUNK_SIZE) if fd.tell() >= fstat.st_size: last = True data = dict(mode='put', data=base64.b64encode(data), out_path=out_path, last=last) if self.runner.become: data['user'] = self.runner.become_user data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise AnsibleError("failed to send the file to %s" % self.host) response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if response.get('failed', False): raise AnsibleError( "failed to put the file in the requested location") finally: fd.close() vvvv("waiting for final response after PUT") response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if response.get('failed', False): raise AnsibleError( "failed to put the file in the requested location")
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' if self.runner.sudo or sudoable and sudo_user: cmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnisbleError("Failed to send command to %s" % self.host) response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) return (response.get('rc', None), '', response.get('stdout', ''), response.get('stderr', ''))
def runtest(modfile, argspath): """Test run a module, piping it's output for reporting.""" os.system("chmod +x %s" % modfile) invoke = "%s" % (modfile) if argspath is not None: invoke = "%s %s" % (modfile, argspath) cmd = subprocess.Popen(invoke, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = cmd.communicate() try: print "***********************************" print "RAW OUTPUT" print out print err results = utils.parse_json(out) except: print "***********************************" print "INVALID OUTPUT FORMAT" print out traceback.print_exc() sys.exit(1) print "***********************************" print "PARSED OUTPUT" print utils.jsonify(results, format=True)
def _get_variables(self, hostname): host = self.get_host(hostname) if host is None: raise errors.AnsibleError("host not found: %s" % hostname) vars = {} for updated in map(lambda x: x.run(host), utils.plugins.vars_loader.all(self)): if updated is not None: vars.update(updated) vars.update(host.get_variables()) if self._is_script: cmd = [self.host_list, "--host", hostname] try: sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError, e: raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e)) (out, err) = sp.communicate() results = utils.parse_json(out) vars.update(results)
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the remote host ''' if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") vvv("EXEC COMMAND %s" % cmd) if self.runner.sudo and sudoable: raise errors.AnsibleError( "When using fireball, do not specify sudo to run your tasks. " + "Instead sudo the fireball action with sudo. " + "Task will communicate with the fireball already running in sudo mode." ) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) self.socket.send(data) response = self.socket.recv() response = utils.decrypt(self.key, response) response = utils.parse_json(response) return (response.get('rc',None), '', response.get('stdout',''), response.get('stderr',''))
def _parse(self): all_hosts = {} self.raw = utils.parse_json(self.data) all=Group('all') groups = dict(all=all) group = None for (group_name, data) in self.raw.items(): group = groups[group_name] = Group(group_name) host = None if not isinstance(data, dict): data = {'hosts': data} if 'hosts' in data: for hostname in data['hosts']: if not hostname in all_hosts: all_hosts[hostname] = Host(hostname) host = all_hosts[hostname] group.add_host(host) if 'vars' in data: for k, v in data['vars'].iteritems(): group.set_variable(k, v) all.add_child_group(group) # Separate loop to ensure all groups are defined for (group_name, data) in self.raw.items(): if isinstance(data, dict) and 'children' in data: for child_name in data['children']: if child_name in groups: groups[group_name].add_child_group(groups[child_name]) return groups
def __init__(self, conn=None, host=None, result=None, comm_ok=True, diff=dict()): # which host is this ReturnData about? if conn is not None: self.host = conn.host delegate = getattr(conn, 'delegate', None) if delegate is not None: self.host = delegate else: self.host = host self.result = result self.comm_ok = comm_ok # if these values are set and used with --diff we can show # changes made to particular files self.diff = diff if type(self.result) in [str, unicode]: self.result = utils.parse_json(self.result) if self.host is None: raise Exception("host not set") if type(self.result) != dict: raise Exception("dictionary result expected")
def _get_variables(self, hostname): host = self.get_host(hostname) if host is None: raise errors.AnsibleError("host not found: %s" % hostname) vars = {} for updated in map(lambda x: x.run(host), utils.plugins.vars_loader.all(self)): if updated is not None: vars.update(updated) if self._is_script: cmd = [self.host_list, "--host", hostname] try: sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError, e: raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e)) (out, err) = sp.communicate() results = utils.parse_json(out) # FIXME: this is a bit redundant with host.py and should share code results['inventory_hostname'] = hostname results['inventory_hostname_short'] = hostname.split('.')[0] groups = [g.name for g in host.get_groups() if g.name != 'all'] results['group_names'] = sorted(groups) vars.update(results)
def _execute_module(self, conn, tmp, remote_module_path, args, async_jid=None, async_module=None, async_limit=None): ''' runs a module that has already been transferred ''' inject = self.setup_cache.get(conn.host,{}).copy() host_variables = self.inventory.get_variables(conn.host) inject.update(host_variables) inject.update(self.module_vars) if self.module_name == 'setup': if not args: args = {} args = self._add_setup_vars(inject, args) args = self._add_setup_metadata(args) if type(args) == dict: args = utils.bigjson(args) args = utils.template(args, inject, self.setup_cache) module_name_tail = remote_module_path.split("/")[-1] argsfile = self._transfer_str(conn, tmp, 'arguments', args) if async_jid is None: cmd = "%s %s" % (remote_module_path, argsfile) else: cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module, argsfile]]) res = self._low_level_exec_command(conn, cmd, tmp, sudoable=True) result1 = utils.parse_json(res) executed_str = "%s %s" % (module_name_tail, args.strip()) return ReturnData(host=conn.host, result=res, executed_str=executed_str)
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the remote host ''' if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported: raise errors.AnsibleError( "Internal Error: this module does not support running commands via %s" % self.runner.become_method) if in_data: raise AnsibleError( "Internal Error: this module does not support optimized module pipelining" ) if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.become and sudoable: cmd, prompt, success_key = utils.make_become_cmd( cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get('rc', None), '', response.get('stdout', ''), response.get('stderr', ''))
def __init__(self, conn=None, host=None, result=None, comm_ok=True, diff=dict()): # which host is this ReturnData about? if conn is not None: self.host = conn.host delegate = getattr(conn, 'delegate', None) if delegate is not None: self.host = delegate else: self.host = host self.result = result self.comm_ok = comm_ok # if these values are set and used with --diff we can show # changes made to particular files self.diff = diff if type(self.result) in [ str, unicode ]: self.result = utils.parse_json(self.result) if self.host is None: raise Exception("host not set") if type(self.result) != dict: raise Exception("dictionary result expected")
def _parse(self, err): all_hosts = {} self.raw = utils.parse_json(self.data) all = Group('all') groups = dict(all=all) group = None if 'failed' in self.raw: sys.stderr.write(err + "\n") raise errors.AnsibleError("failed to parse executable inventory script results: %s" % self.raw) for (group_name, data) in self.raw.items(): # in Ansible 1.3 and later, a "_meta" subelement may contain # a variable "hostvars" which contains a hash for each host # if this "hostvars" exists at all then do not call --host for each # host. This is for efficiency and scripts should still return data # if called with --host for backwards compat with 1.2 and earlier. if group_name == '_meta': if 'hostvars' in data: self.host_vars_from_top = data['hostvars'] continue group = groups[group_name] = Group(group_name) host = None if not isinstance(data, dict): data = {'hosts': data} elif not any(k in data for k in ('hosts','vars')): data = {'hosts': [group_name], 'vars': data} if 'hosts' in data: for hostname in data['hosts']: if not hostname in all_hosts: all_hosts[hostname] = Host(hostname) host = all_hosts[hostname] group.add_host(host) if 'vars' in data: for k, v in data['vars'].iteritems(): if group.name == all.name: all.set_variable(k, v) else: group.set_variable(k, v) if group.name != all.name: all.add_child_group(group) # Separate loop to ensure all groups are defined for (group_name, data) in self.raw.items(): if group_name == '_meta': continue if isinstance(data, dict) and 'children' in data: for child_name in data['children']: if child_name in groups: groups[group_name].add_child_group(groups[child_name]) return groups
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' vvv("EXEC COMMAND %s" % cmd) if self.runner.sudo and sudoable: raise errors.AnsibleError( "fireball does not use sudo, but runs as whoever it was initiated as. (That itself is where to use sudo)." ) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) self.socket.send(data) response = self.socket.recv() response = utils.decrypt(self.key, response) response = utils.parse_json(response) return (response.get('rc', None), '', response.get('stdout', ''), response.get('stderr', ''))
def exec_command(self, cmd, tmp_path, become_user, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the remote host ''' if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") vvv("EXEC COMMAND %s" % cmd) if self.runner.become and sudoable: raise errors.AnsibleError( "When using fireball, do not specify sudo or su to run your tasks. " + "Instead sudo the fireball action with sudo. " + "Task will communicate with the fireball already running in sudo mode." ) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) self.socket.send(data) response = self.socket.recv() response = utils.decrypt(self.key, response) response = utils.parse_json(response) return (response.get('rc',None), '', response.get('stdout',''), response.get('stderr',''))
def _state_func(state, **kws): """Map salt state invocation to ansible module invocation.""" state = kws.pop('fun') if __opts__['test']: return dict(result=None, changes={}, name=state, comment='test is not supported by Ansible modules!') argline = kws.pop('args', '') for k in SALT_KEYS: del kws[k] # detect and translate argument 'name_' into 'name' NAME_ARG = 'name_' if NAME_ARG in kws: kws['name'] = kws[NAME_ARG] del kws[NAME_ARG] modpath = os.path.join(ANSIBLE_MOD_DIR, STATE_NAMES[state]) output = parse_json(run(modpath, argline, kws, raise_exc=True)) ret = dict(name=state, result=False, changes={}, comment='') if 'failed' not in output: ret['result'] = True if state in ('command', 'shell') and output['rc'] != 0: ret['result'] = False if 'msg' in output: ret['comment'] = output['msg'] elif state in ('command', 'shell') and output['stderr']: ret['comment'] = output['stderr'] if ret.get('changed', True): ret['changes'] = dict(ansible=output) return ret
def put_file(self, in_path, out_path): ''' transfer a file from local to remote ''' vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path) fd = file(in_path, 'rb') fstat = os.stat(in_path) try: vvv("PUT file is %d bytes" % fstat.st_size) last = False while fd.tell() <= fstat.st_size and not last: vvvv("file position currently %ld, file size is %ld" % (fd.tell(), fstat.st_size)) data = fd.read(CHUNK_SIZE) if fd.tell() >= fstat.st_size: last = True data = dict(mode='put', data=base64.b64encode(data), out_path=out_path, last=last) if self.runner.sudo: data['user'] = self.runner.sudo_user data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError("failed to send the file to %s" % self.host) response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if response.get('failed',False): raise errors.AnsibleError("failed to put the file in the requested location") finally: fd.close() vvvv("waiting for final response after PUT") response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if response.get('failed',False): raise errors.AnsibleError("failed to put the file in the requested location")
def _return_from_module(self, conn, host, result, executed=None): """ helper function to handle JSON parsing of results """ try: result = utils.parse_json(result) if executed is not None: result["invocation"] = executed return [host, True, result] except Exception, e: return [host, False, "%s/%s/%s" % (str(e), result, executed)]
def _return_from_module(self, conn, host, result, err, executed=None): ''' helper function to handle JSON parsing of results ''' try: result = utils.parse_json(result) if executed is not None: result['invocation'] = executed if 'stderr' in result: err="%s%s"%(err,result['stderr']) return [host, True, result, err] except Exception, e: return [host, False, "%s/%s/%s" % (str(e), result, executed), err]
def __init__(self, host=None, result=None, comm_ok=True, executed_str=''): self.host = host self.result = result self.comm_ok = comm_ok self.executed_str = executed_str if type(self.result) in [str, unicode]: self.result = utils.parse_json(self.result) if host is None: raise Exception("host not set") if type(self.result) != dict: raise Exception("dictionary result expected")
def __init__(self, host=None, result=None, comm_ok=True, executed_str=''): self.host = host self.result = result self.comm_ok = comm_ok self.executed_str = executed_str if type(self.result) in [ str, unicode ]: self.result = utils.parse_json(self.result) if host is None: raise Exception("host not set") if type(self.result) != dict: raise Exception("dictionary result expected")
def _execute_module(self, conn, tmp, module_name, args, async_jid=None, async_module=None, async_limit=None, inject=None, persist_files=False, complex_args=None): ''' runs a module that has already been transferred ''' # hack to support fireball mode if module_name == 'fireball': args = "%s password=%s" % (args, base64.b64encode(str(utils.key_for_hostname(conn.host)))) if 'port' not in args: args += " port=%s" % C.ZEROMQ_PORT (remote_module_path, is_new_style, shebang) = self._copy_module(conn, tmp, module_name, args, inject, complex_args) environment_string = self._compute_environment_string(inject) cmd_mod = "" if self.sudo and self.sudo_user != 'root': # deal with possible umask issues once sudo'ed to other user cmd_chmod = "chmod a+r %s" % remote_module_path self._low_level_exec_command(conn, cmd_chmod, tmp, sudoable=False) cmd = "" if not is_new_style: if 'CHECKMODE=True' in args: # if module isn't using AnsibleModuleCommon infrastructure we can't be certain it knows how to # do --check mode, so to be safe we will not run it. return ReturnData(conn=conn, result=dict(skippped=True, msg="cannot run check mode against old-style modules")) args = utils.template(self.basedir, args, inject) argsfile = self._transfer_str(conn, tmp, 'arguments', args) if async_jid is None: cmd = "%s %s" % (remote_module_path, argsfile) else: cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module, argsfile]]) else: if async_jid is None: cmd = "%s" % (remote_module_path) else: cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module]]) if not shebang: raise errors.AnsibleError("module is missing interpreter line") cmd = " ".join([environment_string, shebang.replace("#!",""), cmd]) if tmp.find("tmp") != -1 and C.DEFAULT_KEEP_REMOTE_FILES != '1' and not persist_files: cmd = cmd + "; rm -rf %s >/dev/null 2>&1" % tmp res = self._low_level_exec_command(conn, cmd, tmp, sudoable=True) data = utils.parse_json(res['stdout']) if 'parsed' in data and data['parsed'] == False: data['msg'] += res['stderr'] return ReturnData(conn=conn, result=data)
def _parse(self, err): all_hosts = {} self.raw = utils.parse_json(self.data) all = Group('all') groups = dict(all=all) group = None if 'failed' in self.raw: sys.stderr.write(err + "\n") raise errors.AnsibleError( "failed to parse executable inventory script results: %s" % self.raw) for (group_name, data) in self.raw.items(): group = groups[group_name] = Group(group_name) host = None if not isinstance(data, dict): data = {'hosts': data} elif not any(k in data for k in ('hosts', 'vars')): data = {'hosts': [group_name], 'vars': data} if 'hosts' in data: for hostname in data['hosts']: if not hostname in all_hosts: all_hosts[hostname] = Host(hostname) host = all_hosts[hostname] group.add_host(host) if 'vars' in data: for k, v in data['vars'].iteritems(): if group.name == all.name: all.set_variable(k, v) else: group.set_variable(k, v) if group.name != all.name: all.add_child_group(group) # Separate loop to ensure all groups are defined for (group_name, data) in self.raw.items(): if isinstance(data, dict) and 'children' in data: for child_name in data['children']: if child_name in groups: groups[group_name].add_child_group(groups[child_name]) return groups
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.sudo and sudoable and sudo_user: cmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get('rc', None), '', response.get('stdout', ''), response.get('stderr', ''))
def _parse(self): groups = {} self.raw = utils.parse_json(self.data) all=Group('all') self.groups = dict(all=all) group = None for (group_name, hosts) in self.raw.items(): group = groups[group_name] = Group(group_name) host = None for hostname in hosts: host = Host(hostname) group.add_host(host) # FIXME: hack shouldn't be needed all.add_host(host) all.add_child_group(group) return groups
def _parse(self): groups = {} self.raw = utils.parse_json(self.data) all = Group('all') self.groups = dict(all=all) group = None for (group_name, hosts) in self.raw.items(): group = groups[group_name] = Group(group_name) host = None for hostname in hosts: host = Host(hostname) group.add_host(host) # FIXME: hack shouldn't be needed all.add_host(host) all.add_child_group(group) return groups
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the remote host ''' if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported: raise AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method) if in_data: raise AnsibleError("Internal Error: this module does not support optimized module pipelining") if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.become and sudoable: cmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get('rc',None), '', response.get('stdout',''), response.get('stderr',''))
def _add_result_to_setup_cache(self, conn, result): ''' allows discovered variables to be used in templates and action statements ''' host = conn.host try: var_result = utils.parse_json(result) except: var_result = {} # note: do not allow variables from playbook to be stomped on # by variables coming up from facter/ohai/etc. They # should be prefixed anyway if not host in self.setup_cache: self.setup_cache[host] = {} for (k, v) in var_result.iteritems(): if not k in self.setup_cache[host]: self.setup_cache[host][k] = v
def put_file(self, in_path, out_path): ''' transfer a file from local to remote ''' vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise errors.AnsibleFileNotFound( "file or module does not exist: %s" % in_path) data = file(in_path).read() data = dict(mode='put', data=data, out_path=out_path) data = utils.jsonify(data) data = utils.encrypt(self.key, data) self.socket.send(data) response = self.socket.recv() response = utils.decrypt(self.key, response) response = utils.parse_json(response)
def fetch_file(self, in_path, out_path): ''' save a remote file to the specified path ''' vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) data = dict(mode='fetch', file=in_path) data = utils.jsonify(data) data = utils.encrypt(self.key, data) self.socket.send(data) response = self.socket.recv() response = utils.decrypt(self.key, response) response = utils.parse_json(response) response = response['data'] fh = open(out_path, "w") fh.write(response) fh.close()
def put_file(self, in_path, out_path): ''' transfer a file from local to remote ''' vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path) data = file(in_path).read() data = dict(mode='put', data=data, out_path=out_path) data = utils.jsonify(data) data = utils.encrypt(self.key, data) self.socket.send(data) response = self.socket.recv() response = utils.decrypt(self.key, response) response = utils.parse_json(response)
def fetch_file(self, in_path, out_path): ''' save a remote file to the specified path ''' vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) data = dict(mode='fetch', in_path=in_path) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError( "failed to initiate the file fetch with %s" % self.host) fh = open(out_path, "w") try: bytes = 0 while True: response = self.recv_data() if not response: raise errors.AnsibleError( "Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if response.get('failed', False): raise errors.AnsibleError( "Error during file fetch, aborting") out = base64.b64decode(response['data']) fh.write(out) bytes += len(out) # send an empty response back to signify we # received the last chunk without errors data = utils.jsonify(dict()) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError( "failed to send ack during file fetch") if response.get('last', False): break finally: # we don't currently care about this final response, # we just receive it and drop it. It may be used at some # point in the future or we may just have the put/fetch # operations not send back a final response at all response = self.recv_data() vvv("FETCH wrote %d bytes to %s" % (bytes, out_path)) fh.close()
def get_variables(self, hostname): if self._is_script: # TODO: move this to inventory_script.py host = self.get_host(hostname) cmd = subprocess.Popen([self.host_list, "--host", hostname], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = cmd.communicate() results = utils.parse_json(out) results['inventory_hostname'] = hostname groups = [g.name for g in host.get_groups() if g.name != 'all'] results['group_names'] = sorted(groups) return results host = self.get_host(hostname) if host is None: raise Exception("host not found: %s" % hostname) return host.get_variables()
def exec_command( self, cmd, tmp_path, sudo_user=None, sudoable=False, executable="/bin/sh", in_data=None, su=None, su_user=None ): """ run a command on the remote host """ if su or su_user: raise AnsibleError("Internal Error: this module does not support running commands via su") if in_data: raise AnsibleError("Internal Error: this module does not support optimized module pipelining") if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.sudo and sudoable and sudo_user: cmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) data = dict(mode="command", cmd=cmd, tmp_path=tmp_path, executable=executable) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get("rc", None), "", response.get("stdout", ""), response.get("stderr", ""))
def validate_user(self): ''' Checks the remote uid of the accelerated daemon vs. the one specified for this play and will cause the accel daemon to exit if they don't match ''' vvvv("%s: sending request for validate_user" % self.host) data = dict( mode='validate_user', username=self.user, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise AnsibleError("Failed to send command to %s" % self.host) vvvv("%s: waiting for validate_user response" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the validate_user response: %s" % (self.host, response)) break if response.get('failed'): return False else: return response.get('rc') == 0
def get_variables(self, hostname): if self._is_script: # TODO: move this to inventory_script.py host = self.get_host(hostname) cmd = subprocess.Popen( [self.host_list,"--host",hostname], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) (out, err) = cmd.communicate() results = utils.parse_json(out) results['inventory_hostname'] = hostname groups = [ g.name for g in host.get_groups() if g.name != 'all' ] results['group_names'] = sorted(groups) return results host = self.get_host(hostname) if host is None: raise Exception("host not found: %s" % hostname) return host.get_variables()
def fetch_file(self, in_path, out_path): ''' save a remote file to the specified path ''' vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) data = dict(mode='fetch', in_path=in_path) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError("failed to initiate the file fetch with %s" % self.host) fh = open(out_path, "w") try: bytes = 0 while True: response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if response.get('failed', False): raise errors.AnsibleError("Error during file fetch, aborting") out = base64.b64decode(response['data']) fh.write(out) bytes += len(out) # send an empty response back to signify we # received the last chunk without errors data = utils.jsonify(dict()) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError("failed to send ack during file fetch") if response.get('last', False): break finally: # we don't currently care about this final response, # we just receive it and drop it. It may be used at some # point in the future or we may just have the put/fetch # operations not send back a final response at all response = self.recv_data() vvv("FETCH wrote %d bytes to %s" % (bytes, out_path)) fh.close()
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.sudo and sudoable and sudo_user: cmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get('rc',None), '', response.get('stdout',''), response.get('stderr',''))
def __init__(self, conn=None, host=None, result=None, comm_ok=True): # which host is this ReturnData about? if conn is not None: self.host = conn.host delegate = getattr(conn, 'delegate', None) if delegate is not None: self.host = delegate else: self.host = host self.result = result self.comm_ok = comm_ok if type(self.result) in [str, unicode]: self.result = utils.parse_json(self.result) if self.host is None: raise Exception("host not set") if type(self.result) != dict: raise Exception("dictionary result expected")
def __init__(self, conn=None, host=None, result=None, comm_ok=True): # which host is this ReturnData about? if conn is not None: delegate_for = getattr(conn, '_delegate_for', None) if delegate_for: self.host = delegate_for else: self.host = conn.host else: self.host = host self.result = result self.comm_ok = comm_ok if type(self.result) in [ str, unicode ]: self.result = utils.parse_json(self.result) if self.host is None: raise Exception("host not set") if type(self.result) != dict: raise Exception("dictionary result expected")
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False): ''' run a command on the remote host ''' vvv("EXEC COMMAND %s" % cmd) if self.runner.sudo and sudoable: raise errors.AnsibleError("fireball does not use sudo, but runs as whoever it was initiated as. (That itself is where to use sudo).") data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) self.socket.send(data) response = self.socket.recv() response = utils.decrypt(self.key, response) response = utils.parse_json(response) return ('', response.get('stdout',''), response.get('stderr',''))
def _get_variables(self, hostname): host = self.get_host(hostname) if host is None: raise errors.AnsibleError("host not found: %s" % hostname) vars = {} for updated in map(lambda x: x.run(host), utils.plugins.vars_loader.all(self)): if updated is not None: vars.update(updated) vars.update(host.get_variables()) if self._is_script: cmd = [self.host_list,"--host",hostname] try: sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError, e: raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e)) (out, err) = sp.communicate() results = utils.parse_json(out) vars.update(results)
def _get_variables(self, hostname): if self._is_script: host = self.get_host(hostname) cmd = subprocess.Popen([self.host_list, "--host", hostname], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = cmd.communicate() results = utils.parse_json(out) # FIXME: this is a bit redundant with host.py and should share code results['inventory_hostname'] = hostname results['inventory_hostname_short'] = hostname.split('.')[0] groups = [g.name for g in host.get_groups() if g.name != 'all'] results['group_names'] = sorted(groups) return results host = self.get_host(hostname) if host is None: raise Exception("host not found: %s" % hostname) return host.get_variables()
def _get_variables(self, hostname): if self._is_script: host = self.get_host(hostname) cmd = subprocess.Popen( [self.host_list,"--host",hostname], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) (out, err) = cmd.communicate() results = utils.parse_json(out) # FIXME: this is a bit redundant with host.py and should share code results['inventory_hostname'] = hostname results['inventory_hostname_short'] = hostname.split('.')[0] groups = [ g.name for g in host.get_groups() if g.name != 'all' ] results['group_names'] = sorted(groups) return results host = self.get_host(hostname) if host is None: raise Exception("host not found: %s" % hostname) return host.get_variables()
def _execute_module(self, conn, tmp, module_name, args, async_jid=None, async_module=None, async_limit=None, inject=None, persist_files=False, complex_args=None): ''' runs a module that has already been transferred ''' # hack to support fireball mode if module_name == 'fireball': args = "%s password=%s" % (args, base64.b64encode(str(utils.key_for_hostname(conn.host)))) if 'port' not in args: args += " port=%s" % C.ZEROMQ_PORT (remote_module_path, module_style, shebang) = self._copy_module(conn, tmp, module_name, args, inject, complex_args) environment_string = self._compute_environment_string(inject) cmd_mod = "" if self.sudo and self.sudo_user != 'root': # deal with possible umask issues once sudo'ed to other user cmd_chmod = "chmod a+r %s" % remote_module_path self._low_level_exec_command(conn, cmd_chmod, tmp, sudoable=False) cmd = "" if module_style != 'new': if 'CHECKMODE=True' in args: # if module isn't using AnsibleModuleCommon infrastructure we can't be certain it knows how to # do --check mode, so to be safe we will not run it. return ReturnData(conn=conn, result=dict(skippped=True, msg="cannot yet run check mode against old-style modules")) args = template.template(self.basedir, args, inject) # decide whether we need to transfer JSON or key=value argsfile = None if module_style == 'non_native_want_json': if complex_args: complex_args.update(utils.parse_kv(args)) argsfile = self._transfer_str(conn, tmp, 'arguments', utils.jsonify(complex_args)) else: argsfile = self._transfer_str(conn, tmp, 'arguments', utils.jsonify(utils.parse_kv(args))) else: argsfile = self._transfer_str(conn, tmp, 'arguments', args) if self.sudo and self.sudo_user != 'root': # deal with possible umask issues once sudo'ed to other user cmd_args_chmod = "chmod a+r %s" % argsfile self._low_level_exec_command(conn, cmd_args_chmod, tmp, sudoable=False) if async_jid is None: cmd = "%s %s" % (remote_module_path, argsfile) else: cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module, argsfile]]) else: if async_jid is None: cmd = "%s" % (remote_module_path) else: cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module]]) if not shebang: raise errors.AnsibleError("module is missing interpreter line") cmd = " ".join([environment_string.strip(), shebang.replace("#!","").strip(), cmd]) cmd = cmd.strip() if tmp.find("tmp") != -1 and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files: if not self.sudo or self.sudo_user == 'root': # not sudoing or sudoing to root, so can cleanup files in the same step cmd = cmd + "; rm -rf %s >/dev/null 2>&1" % tmp sudoable = True if module_name == "accelerate": # always run the accelerate module as the user # specified in the play, not the sudo_user sudoable = False res = self._low_level_exec_command(conn, cmd, tmp, sudoable=sudoable) if self.sudo and self.sudo_user != 'root': # not sudoing to root, so maybe can't delete files as that other user # have to clean up temp files as original user in a second step if tmp.find("tmp") != -1 and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files: cmd2 = "rm -rf %s >/dev/null 2>&1" % tmp self._low_level_exec_command(conn, cmd2, tmp, sudoable=False) data = utils.parse_json(res['stdout']) if 'parsed' in data and data['parsed'] == False: data['msg'] += res['stderr'] return ReturnData(conn=conn, result=data)
for child_name in data['children']: if child_name in groups: groups[group_name].add_child_group(groups[child_name]) for group in groups.values(): if group.depth == 0 and group.name != 'all': all.add_child_group(group) return groups def get_host_variables(self, host): """ Runs <script> --host <hostname> to determine additional host variables """ if self.host_vars_from_top is not None: got = self.host_vars_from_top.get(host.name, {}) return got cmd = [self.filename, "--host", host.name] try: sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError, e: raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e)) (out, err) = sp.communicate() if out.strip() == '': return dict() try: return json_dict_unicode_to_bytes(utils.parse_json(out)) except ValueError: raise errors.AnsibleError("could not parse post variable response: %s, %s" % (cmd, out))
all.set_variable(k, v) else: group.set_variable(k, v) if group.name != all.name: all.add_child_group(group) # Separate loop to ensure all groups are defined for (group_name, data) in self.raw.items(): if group_name == '_meta': continue if isinstance(data, dict) and 'children' in data: for child_name in data['children']: if child_name in groups: groups[group_name].add_child_group(groups[child_name]) return groups def get_host_variables(self, host): """ Runs <script> --host <hostname> to determine additional host variables """ if self.host_vars_from_top is not None: got = self.host_vars_from_top.get(host.name, {}) return got cmd = [self.filename, "--host", host.name] try: sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError, e: raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e)) (out, err) = sp.communicate() return utils.parse_json(out)
def _parse(self, err): all_hosts = {} # not passing from_remote because data from CMDB is trusted self.raw = utils.parse_json(self.data) self.raw = json_dict_bytes_to_unicode(self.raw) all = Group('all') groups = dict(all=all) group = None if 'failed' in self.raw: sys.stderr.write(err + "\n") raise errors.AnsibleError("failed to parse executable inventory script results: %s" % self.raw) for (group_name, data) in self.raw.items(): # in Ansible 1.3 and later, a "_meta" subelement may contain # a variable "hostvars" which contains a hash for each host # if this "hostvars" exists at all then do not call --host for each # host. This is for efficiency and scripts should still return data # if called with --host for backwards compat with 1.2 and earlier. if group_name == '_meta': if 'hostvars' in data: self.host_vars_from_top = data['hostvars'] continue if group_name != all.name: group = groups[group_name] = Group(group_name) else: group = all host = None if not isinstance(data, dict): data = {'hosts': data} # is not those subkeys, then simplified syntax, host with vars elif not any(k in data for k in ('hosts','vars')): data = {'hosts': [group_name], 'vars': data} if 'hosts' in data: if not isinstance(data['hosts'], list): raise errors.AnsibleError("You defined a group \"%s\" with bad " "data for the host list:\n %s" % (group_name, data)) for hostname in data['hosts']: if not hostname in all_hosts: all_hosts[hostname] = Host(hostname) host = all_hosts[hostname] group.add_host(host) if 'vars' in data: if not isinstance(data['vars'], dict): raise errors.AnsibleError("You defined a group \"%s\" with bad " "data for variables:\n %s" % (group_name, data)) for k, v in data['vars'].iteritems(): if group.name == all.name: all.set_variable(k, v) else: group.set_variable(k, v) # Separate loop to ensure all groups are defined for (group_name, data) in self.raw.items(): if group_name == '_meta': continue if isinstance(data, dict) and 'children' in data: for child_name in data['children']: if child_name in groups: groups[group_name].add_child_group(groups[child_name]) for group in groups.values(): if group.depth == 0 and group.name != 'all': all.add_child_group(group) return groups
def _execute_module(self, conn, tmp, module_name, args, async_jid=None, async_module=None, async_limit=None, inject=None, persist_files=False, complex_args=None): ''' runs a module that has already been transferred ''' # hack to support fireball mode if module_name == 'fireball': args = "%s password=%s" % (args, base64.b64encode(str(utils.key_for_hostname(conn.host)))) if 'port' not in args: args += " port=%s" % C.ZEROMQ_PORT (remote_module_path, module_style, shebang) = self._copy_module(conn, tmp, module_name, args, inject, complex_args) environment_string = self._compute_environment_string(inject) cmd_mod = "" if self.sudo and self.sudo_user != 'root': # deal with possible umask issues once sudo'ed to other user cmd_chmod = "chmod a+r %s" % remote_module_path self._low_level_exec_command(conn, cmd_chmod, tmp, sudoable=False) cmd = "" if module_style != 'new': if 'CHECKMODE=True' in args: # if module isn't using AnsibleModuleCommon infrastructure we can't be certain it knows how to # do --check mode, so to be safe we will not run it. return ReturnData(conn=conn, result=dict(skippped=True, msg="cannot yet run check mode against old-style modules")) args = template.template(self.basedir, args, inject) # decide whether we need to transfer JSON or key=value argsfile = None if module_style == 'non_native_want_json': if complex_args: complex_args.update(utils.parse_kv(args)) argsfile = self._transfer_str(conn, tmp, 'arguments', utils.jsonify(complex_args)) else: argsfile = self._transfer_str(conn, tmp, 'arguments', utils.jsonify(utils.parse_kv(args))) else: argsfile = self._transfer_str(conn, tmp, 'arguments', args) if async_jid is None: cmd = "%s %s" % (remote_module_path, argsfile) else: cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module, argsfile]]) else: if async_jid is None: cmd = "%s" % (remote_module_path) else: cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module]]) if not shebang: raise errors.AnsibleError("module is missing interpreter line") cmd = " ".join([environment_string.strip(), shebang.replace("#!","").strip(), cmd]) cmd = cmd.strip() if tmp.find("tmp") != -1 and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files: if not self.sudo or self.sudo_user == 'root': # not sudoing or sudoing to root, so can cleanup files in the same step cmd = cmd + "; rm -rf %s >/dev/null 2>&1" % tmp sudoable = True if module_name == "accelerate": # always run the accelerate module as the user # specified in the play, not the sudo_user sudoable = False res = self._low_level_exec_command(conn, cmd, tmp, sudoable=sudoable) if self.sudo and self.sudo_user != 'root': # not sudoing to root, so maybe can't delete files as that other user # have to clean up temp files as original user in a second step if tmp.find("tmp") != -1 and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files: cmd2 = "rm -rf %s >/dev/null 2>&1" % tmp self._low_level_exec_command(conn, cmd2, tmp, sudoable=False) data = utils.parse_json(res['stdout']) if 'parsed' in data and data['parsed'] == False: data['msg'] += res['stderr'] return ReturnData(conn=conn, result=data)
def _parse(self, err): all_hosts = {} # not passing from_remote because data from CMDB is trusted self.raw = utils.parse_json(self.data) # 还是使用self.data来解析标准输出,需要self.data为json格式数据 self.raw = json_dict_bytes_to_unicode(self.raw) # 将self.raw中的kv都转换成unicode格式。 all = Group('all') # 设置Group("all") groups = dict(all=all) # 初始化groups字典 group = None if 'failed' in self.raw: # 如果self.raw中有failed字段,则报错。不过在上面parser_json的时候no_exception是false,不会出现failed情况 sys.stderr.write(err + "\n") raise errors.AnsibleError("failed to parse executable inventory script results: %s" % self.raw) for (group_name, data) in self.raw.items(): # 1.3 以上版本使用--list的时返回结果中会包含_meta这样的key,该key的value中会有一个hostvars变量,该变量包含每个host的主机变量 # 1.2及以下版本仍然需要使用--host命令为每一个host返回主机变量 # in Ansible 1.3 and later, a "_meta" subelement may contain # a variable "hostvars" which contains a hash for each host # if this "hostvars" exists at all then do not call --host for each # host. This is for efficiency and scripts should still return data # if called with --host for backwards compat with 1.2 and earlier. """ { "databases" : { "hosts" : [ "host1.example.com", "host2.example.com" ], "vars" : { "a" : true } }, "webservers" : [ "host2.example.com", "host3.example.com" ], "atlanta" : { "hosts" : [ "host1.example.com", "host4.example.com", "host5.example.com" ], "vars" : { "b" : false }, "children": [ "marietta", "5points" ] }, "marietta" : [ "host6.example.com" ], "5points" : [ "host7.example.com" ] } { # results of inventory script as above go here # ... "_meta" : { "hostvars" : { "moocow.example.com" : { "asdf" : 1234 }, "llama.example.com" : { "asdf" : 5678 }, } } { "moocow.example.com" : { "asdf" : 1234 }, "llama.example.com" : { "asdf" : 5678 } } """ if group_name == '_meta': # 如果key为_meta,则该value为meta数据。 if 'hostvars' in data: self.host_vars_from_top = data['hostvars'] # 如果meta数据中包含hostvars,则缓存到self.host_vars_from_top中。 continue # 跳过之后的处理 if group_name != all.name: # group_name不是all则创建新的Group对象,并加入groups字典。 group = groups[group_name] = Group(group_name) # 如果data中出现无group的hostname,则会出现以hostname为名称的组 else: group = all host = None if not isinstance(data, dict): # 如果data不是字典类型,则表示data为主机列表 data = {'hosts': data} # is not those subkeys, then simplified syntax, host with vars elif not any(k in data for k in ('hosts','vars','children')): # any函数表示可迭代对象中任何一个为True,则为True.否则为False # 如果data对象是字典类型,但key中不存在hosts、vars、children时,既该行数据为主机变量数据时,group_name为主机名 data = {'hosts': [group_name], 'vars': data} # 上面两步为了统一data的格式,不带变量的data,格式为{'hosts': [host1,host2...] # 带变量的data,格式为: {'hosts': [group_name], 'vars': data } if 'hosts' in data: if not isinstance(data['hosts'], list): # 主机列表必须是list对象,上面的'hosts': [group_name] 也是为校验统一格式 raise errors.AnsibleError("You defined a group \"%s\" with bad " "data for the host list:\n %s" % (group_name, data)) for hostname in data['hosts']: # 遍历主机名 if not hostname in all_hosts: all_hosts[hostname] = Host(hostname) # 如果主机对象不存在, 则创建并添加到all_hosts列表中,去重 host = all_hosts[hostname] # 将Host对象赋值给host变量 group.add_host(host) # 将Host对象添加到该group中 if 'vars' in data: # 如果是带有vars的data if not isinstance(data['vars'], dict): raise errors.AnsibleError("You defined a group \"%s\" with bad " "data for variables:\n %s" % (group_name, data)) for k, v in data['vars'].iteritems(): # 遍历所有的变量 if group.name == all.name: # 如果当前组名为“all",则将变量加到all group中,否则加到当前group中。 all.set_variable(k, v) else: group.set_variable(k, v) # Separate loop to ensure all groups are defined for (group_name, data) in self.raw.items(): if group_name == '_meta': continue if isinstance(data, dict) and 'children' in data: # 如果data中包含子组,遍历子组并将子组添加到父组中 for child_name in data['children']: if child_name in groups: groups[group_name].add_child_group(groups[child_name]) for group in groups.values(): if group.depth == 0 and group.name != 'all': # 如果该组深度为0,且名称不是"all",则加入all组的子组列表中。 all.add_child_group(group) return groups
for child_name in data['children']: if child_name in groups: groups[group_name].add_child_group(groups[child_name]) for group in groups.values(): if group.depth == 0 and group.name != 'all': all.add_child_group(group) return groups def get_host_variables(self, host): """ Runs <script> --host <hostname> to determine additional host variables """ if self.host_vars_from_top is not None: got = self.host_vars_from_top.get(host.name, {}) return got cmd = [self.filename, "--host", host.name] try: sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError, e: raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e)) (out, err) = sp.communicate() if out.strip() == '': return dict() try: return json_dict_bytes_to_unicode(utils.parse_json(out)) except ValueError: raise errors.AnsibleError("could not parse post variable response: %s, %s" % (cmd, out))