def render(self): rendered = defaultlist(list) columns = [] def _get_value(data, value): ret = data.get(value) if ret is None: ret = '' return ret for column in self.columns: rows = [_get_value(data, column.name) for data in self.data] if not any(filter(lambda i: i != '', rows)) and column.drop_if_empty: continue columns.append(column) if column.max_width is None: column.max_width = self.max_col_width if column.align is None: column.align = self.align if column.header_align is None: column.header_align = self.header_align if column.padding is None: column.padding = self.padding raw_data = [column.title] + rows colored_data = [colored(str(data)) for data in raw_data] uncolored_data = [uncolorize(data) for data in colored_data] max_width = column.max_width or max( len(data) for data in uncolored_data) for i, data in enumerate(colored_data): align = column.header_align if i == 0 else column.align coloring_spacing = len(colored_data[i]) - len( uncolored_data[i]) spacing = max_width + coloring_spacing format_string = "{{data:{align}{spacing}}}".format( align=self._ALIGN_MAP[align], spacing=spacing) rendered[i].append(format_string.format(data=data)) output = StringIO() for r_i, row in enumerate(rendered): r_parts = [] for col_i, col in enumerate(row): column = columns[col_i] padding = column.padding * " " if column.max_width and r_i > 0: col = compact(col, column.max_width, suffix_length=column.max_width // 10) r_parts.append("{padding}{col}{padding}".format( col=col, padding=padding)) output.write("|".join(r_parts)) output.write("\n") if r_i == 0: output.seek(0) first_row = output.read() output.write(len(uncolorize(first_row)) * '-' + "\n") output.seek(0) return output.read()
def run(self, host_id, *args, name=None, timeout=HOUR, server_timeout=True, line_timeout=None, log_file=None, raise_on_failure=True, max_output_per_channel=None, set_new_logpath=None): """ Run a command on a specific host :param [str] host_id: The ID of the host to send the command to :param [List[str]] args: The command to run on the host (e.g. 'echo', 'hello') :param [str] name: A name to give to the command (for logging pruposes) :param [int, float] timeout: Command timeout in seconds :param [int, float] server_timeout: Command timeout in seconds to be enforced in the agent :param [int, float] line_timeout: Command output timeout in seconds :param [str] log_file: Log file path for writing command output on the remote host :param [bool] raise_on_failure: Should the client raise an appropriate exception on failure :param [int] max_output_per_channel: Maximum command output in bytes :param [str] set_new_logpath: Reconfigure the remote agent's log path :returns: The command running on the remote host :rtype: Cmd """ if not name: lines = " ".join(args).strip().splitlines() name = compact(lines.pop(0), 60, suffix_length=10) if lines: name += " ..." name = "`%s`" % name cmd = Cmd(talker=self, host_id=host_id, raise_on_failure=raise_on_failure, line_timeout=line_timeout, log_file=log_file, args=args, timeout=timeout, server_timeout=server_timeout, name=name, max_output_per_channel=max_output_per_channel, set_new_logpath=set_new_logpath) cmd.send() return cmd
def send(self): """ Send the command to the Talker agent on the host """ args = [str(a) for a in self.args] command_string = ' '.join(x if ' ' not in x else '"%s"' % x for x in args) timeout = self.timeout if self.server_timeout else None extras = {} if self.kwargs_resilient: extras.update( max_output_per_channel=self.max_output_per_channel, set_new_logpath=self.set_new_logpath, line_timeout=self.line_timeout, log_file=self.log_file, ) self.put_command(id=self.job_id, cmd=args, timeout=timeout, job_repr=compact(command_string, 100), **extras)
def compacted(s): return compact(str(s).split("\n", 1)[0], 20, "....", 5).strip()