Skip to content
Snippets Groups Projects
list.py 13.4 KiB
Newer Older
Seblu's avatar
Seblu committed
#!/usr/bin/env python
#coding=utf8

'''
CloudControl list command
'''

from cccli.exception import *
from sjrpc.core.exceptions import *
from cccli.printer import Printer, color
Seblu's avatar
Seblu committed
from cccli.command import TqlCommand
Seblu's avatar
Seblu committed
import math
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
class Command_list(TqlCommand):
Seblu's avatar
Seblu committed
    '''List objects'''

    def __init__(self, cli, argv0):
Seblu's avatar
Seblu committed
        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")
Seblu's avatar
Seblu committed
    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:
Seblu's avatar
Seblu committed
            return
            self.list_align(objs)
            self.list_table(objs)
        elif self.options.mikrotik:
            self.list_mikrotik(objs)
        elif self.options.vertical:
            self.list_vertical(objs)
Seblu's avatar
Seblu committed
        else:
Seblu's avatar
Seblu committed
            self.print_objects(objs)
Seblu's avatar
Seblu committed

    def remote_functions(self):
        return set(("list",))

    def list_align(self, objs):
        '''Listing line aligned'''
        term_height, term_width = self.printer.get_term_size()
Seblu's avatar
Seblu committed
        # 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
Seblu's avatar
Seblu committed
        tags = dict()
        for o in objs["objects"]:
            for (t,v) in o.items():
Seblu's avatar
Seblu committed
                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)))
Seblu's avatar
Seblu committed
        # compute index width
        indexw = int(math.log10(len(objs["objects"]))) + 1
        # dislay each object by line
Seblu's avatar
Seblu committed
        for (i,o) in enumerate(objs["objects"]):
            line_pos = 0
            num_pos = 0
            # tag position on the line
            pos = dict()
Seblu's avatar
Seblu committed
            if self.options.index:
                line = ("[%d]"%i).ljust(indexw + 3)
                line_pos = len(line)
Seblu's avatar
Seblu committed
            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),
Seblu's avatar
Seblu committed
                                      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"]))

Seblu's avatar
Seblu committed

    def list_table(self, objs):
        '''Listing table style'''
        term_height, term_width = self.printer.get_term_size()
Seblu's avatar
Seblu committed
        # 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)
Seblu's avatar
Seblu committed
        tags = dict()
        for o in objs["objects"]:
            for (t,v) in o.items():
Seblu's avatar
Seblu committed
                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
Seblu's avatar
Seblu committed
                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
Seblu's avatar
Seblu committed
        # 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")
Seblu's avatar
Seblu committed
        # 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 ":"
Seblu's avatar
Seblu committed
                    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