#!/usr/bin/env python #coding=utf8 ''' CloudControl list command ''' from cccli.exception import * from sjrpc.core.exceptions import * from cccli.printer import Printer, color from cccli.command import TqlCommand import math import os class Command_list(TqlCommand): '''List objects''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.set_usage("%prog [options] [tql]") self.add_option("-t", action="store_true", dest="table", help="column aligment display") self.add_option("-l", action="store_true", dest="align", help="line aligment display") self.add_option("-v", action="store_true", dest="vertical", help="vertical display") self.add_option("-m", action="store_true", dest="mikrotik", help="mikrotik display") self.remove_option("--quiet") self.remove_option("--direct") def __call__(self, argv): self.parse_args(argv) if len(self.args) == 0: self.args.append("") # smart merge argument to have a tql tql = "" for arg in self.args: if len(arg) == 0 or arg[0] in ("&", "|", "$", "^", "%"): tql += arg else: tql += "&%s" % arg objs = self.rpccall("list", tql, _status=False, _direct=True) if len(objs["objects"]) == 0: return if self.options.align: self.list_align(objs) elif self.options.table: self.list_table(objs) elif self.options.mikrotik: self.list_mikrotik(objs) elif self.options.vertical: self.list_vertical(objs) else: self.print_objects(objs) def remote_functions(self): return set(("list",)) def list_align(self, objs): '''Listing line aligned''' term_height, term_width = self.printer.get_term_size() # remove ignored tags for tag in self.options.ignore: if tag in objs["order"]: objs["order"].remove(tag) for row in objs["objects"]: if tag in row: row.pop(tag) # get max size by tag tags = dict() for o in objs["objects"]: for (t,v) in o.items(): tags[t] = max(len(self.tdr(t, v)), tags.get(t, 0)) # build initial print order order = [ t for t in objs.get("order", []) if t in tags ] order.extend(sorted(set(tags.keys()) - set(order))) # compute index width indexw = int(math.log10(len(objs["objects"]))) + 1 # dislay each object by line for (i,o) in enumerate(objs["objects"]): line_pos = 0 num_pos = 0 # tag position on the line pos = dict() if self.options.index: line = ("[%d]"%i).ljust(indexw + 3) line_pos = len(line) else: line = "" # variable for create a newline new_line = False first_line = True for t in order: # if tag doesn't fit into the space left, newline if line_pos + len(t) + len(":") + tags[t] + len(" ") >= term_width or new_line: line += os.linesep line_pos = 0 num_pos = 0 new_line = False first_line = False line += "%s%s:%s%s "%(self.tdtc(t), t, self.tdc(t), self.tdr(t, o.get(t, u"")).ljust(tags[t])) pos[num_pos] = line_pos line_pos += len(t) + len(":") + tags[t] + len(" ") num_pos += 1 # align the next tag on the tag above and right tmp_pos = num_pos found_pos = False while tmp_pos in pos: # if the tag position above is greater than the line position, add space to align the next tag if pos[tmp_pos] > line_pos: line += " " * (pos[tmp_pos] - line_pos) line_pos = pos[tmp_pos] found_pos = True break else: tmp_pos += 1 new_line = not found_pos and not first_line self.printer.out("%s%s"%(line, color["reset"])) if not self.options.nocount: self.printer.out("Objects count: %s" % len(objs["objects"])) def list_table(self, objs): '''Listing table style''' term_height, term_width = self.printer.get_term_size() # remove ignored tags for tag in self.options.ignore: if tag in objs["order"]: objs["order"].remove(tag) for row in objs["objects"]: if tag in row: row.pop(tag) # get max size by tag tags = dict() for o in objs["objects"]: for (t,v) in o.items(): tags[t] = max(len(self.tdr(t, v)), tags.get(t, len(t))) size_id = tags["id"] + len(" ") # compute index width indexw = max(int(math.log10(len(objs["objects"]))+1), len("index ")) if size_id + indexw >= term_width: raise cmdError("term width is too small") # build initial print order order = [ t for t in objs.get("order", []) if t in tags ] order.extend(sorted(set(tags.keys()) - set(order))) # remove id of order order = order[1:] # print tag table by title group while order: tag_index = 0 line_pos = 0 if self.options.index: # print index title self.printer.out("index ", nl="") line_pos = indexw # print id title self.printer.out(self.tdtc("id") + "id".ljust(tags["id"]), nl=" ") line_pos += size_id # print tags title section in order for t in order: # if the tag don't fit in space left, stop the title loop if line_pos + tags[t] + len(" ") > term_width and tag_index != 0: break self.printer.out(self.tdtc(t) + t.ljust(tags[t]), nl=" ") line_pos += tags[t] + len(" ") tag_index += 1 self.printer.out(color["reset"]) # print tags corresponding to the title for (i,o) in enumerate(objs["objects"]): line_pos = 0 if self.options.index: self.printer.out(("%d "%i).ljust(indexw), nl="") line_pos += indexw # print id value self.printer.out(self.tdc("id") + self.tdr("id", o.get("id", u"")).ljust(tags["id"]), nl=" ") line_pos += size_id # print tag value for t in order[:tag_index]: self.printer.out(self.tdc(t), nl="") buf, line_pos = self._format_indent_text(self.tdr(t, o.get(t, u"")).ljust(tags[t]) + " ", line_pos, size_id, term_width) self.printer.out(buf, nl="") self.printer.out(color["reset"]) order = order[tag_index:] if not self.options.nocount: self.printer.out("Objects count: %s" % len(objs["objects"])) def list_vertical(self, objs): '''Vertical display''' term_height, term_width = self.printer.get_term_size() # set margin for tags margin = 3 # remove ignored tags for tag in self.options.ignore: if tag in objs["order"]: objs["order"].remove(tag) for row in objs["objects"]: if tag in row: row.pop(tag) # build full tag order list: tags = set((t for o in objs['objects'] for t in o.keys())) order = [t for t in objs.get("order", []) if t in tags] order.extend(sorted(tags - set(order))) # compute index width indexw = int(math.log10(len(objs["objects"]))) + 1 for (i, o) in enumerate(objs["objects"]): line = "" if self.options.index: # +3 is the len("["), len("]"), len(" ") line = ("[%d]"%i).ljust(indexw + 3) # write id tag = order[0] line += "%s%s:%s%s "%(self.tdtc(tag), tag, self.tdc(tag), self.tdr(tag, o.get(tag))) line += os.linesep for t in order[1:]: # write tag if o.get(t) is not None: # write tag title buf, line_pos = self._format_indent_text(t + ":", margin, margin, term_width) line += " " * margin + self.tdtc(t) + buf # write tag value buf, line_pos = self._format_indent_text(self.tdr(t, o.get(t)) + " ", line_pos, margin, term_width) line += self.tdc(t) if line_pos != margin: line += (buf + os.linesep) else: line += (buf[:-(margin + 1)] + os.linesep) self.printer.out("%s%s"%(line[:-1], color["reset"])) if not self.options.nocount: self.printer.out("Objects count: %s" % len(objs["objects"])) def list_mikrotik(self, objs): '''Mikrotik style display''' term_height, term_width = self.printer.get_term_size() # check if the line width is not too small for this kind of listing, # 10 seem to be a good minimum value: if term_width < 10: raise cmdError("term width is too small") # remove ignored tags for tag in self.options.ignore: if tag in objs["order"]: objs["order"].remove(tag) for row in objs["objects"]: if tag in row: row.pop(tag) # calculate the size of the marge, the taken value is equal to the # maximum id length, capped to the 1/3 of the terminal width: list_id = [len(o['id']) for o in objs['objects'] if len(o['id']) < term_width / 3] if list_id: margin = max(list_id) + 4 else: margin = term_width / 3 # compute index width (part of the margin if apply): if self.options.index: indexw = int(math.log10(len(objs["objects"]))) + 1 margin += indexw # build full tag order list: tags = set((t for o in objs['objects'] for t in o.keys())) order = [t for t in objs.get("order", []) if t in tags] order.extend(sorted(tags - set(order))) # dislay each object for i, o in enumerate(objs["objects"]): line_pos = 0 line = "" if self.options.index: line = ("[%d]"%i).ljust(indexw + 3) line_pos = len(line) # write the id tag on the margin: tag = order[0] # +2 is the size of space and ":" id_size = line_pos + len(tag) + len(self.tdr(tag, o.get(tag))) + 2 line += "%s%s:%s%s "%(self.tdtc(tag), tag, self.tdc(tag), self.tdr(tag, o.get(tag, u""))) line_pos = margin if id_size <= term_width / 3: line += " " * (margin - id_size) else: line += os.linesep + " " * margin # write all other tags after id: for tag in order[1:]: if o.get(tag) is not None: # the +2 is the size of space and ":" tag_size = len(tag) + len(self.tdr(tag, o.get(tag))) + 2 # if tag doesn't fit into the space left on current line, # we jump on a new line: if line_pos + tag_size > term_width and margin != line_pos: line_pos = margin line += os.linesep + " " * margin # write tag name: buf, line_pos = self._format_indent_text(tag + ":", line_pos, margin, term_width) line += self.tdtc(tag) + buf # write tag value: buf, line_pos = self._format_indent_text(self.tdr(tag, o.get(tag)) + " ", line_pos, margin, term_width) line += self.tdc(tag) + buf self.printer.out("%s%s%s"%(line, color["reset"], os.linesep)) if not self.options.nocount: self.printer.out("Objects count: %s" % len(objs["objects"])) def _format_indent_text(self, text, current_pos, min_pos, max_pos): '''Reformat text to start in current_pos and fit in column between min_pos and max_pos For example format_indent_text("tototatatiti", 3, 2, 6) return line = "tot\n otat\n atit\n i", current_pos = 3''' buf = "" last_size = 0 while text: # the loop insert newline and indent in text tmp = text[:max_pos - current_pos] text = text[max_pos - current_pos:] last_size = current_pos + len(tmp) buf += tmp # if next text is not empty, newline and indent if text != "": buf += os.linesep + " " * min_pos # if next text is just a space, stop loop if text == " ": last_size = min_pos break current_pos = min_pos return buf, last_size