diff --git a/qubesadmin/tools/qvm_ls.py b/qubesadmin/tools/qvm_ls.py index 38b39eae..00fe4a07 100644 --- a/qubesadmin/tools/qvm_ls.py +++ b/qubesadmin/tools/qvm_ls.py @@ -404,14 +404,15 @@ class Table(object): :param list colnames: Names of the columns (need not to be uppercase). ''' def __init__(self, domains, colnames, spinner, *, raw_data=False, - tree_sorted=False, sort_order='NAME', reverse_sort=False, - ignore_case=False): + tree_sorted=False, ttree_sorted=False, sort_order='NAME', + reverse_sort=False, ignore_case=False): self.domains = domains self.columns = tuple(Column.columns[col.upper().replace('_', '-')] for col in colnames) self.spinner = spinner self.raw_data = raw_data self.tree_sorted = tree_sorted + self.ttree_sorted = ttree_sorted self.sort_order = sort_order self.reverse_sort = reverse_sort self.ignore_case = ignore_case @@ -424,7 +425,7 @@ def get_row(self, vm, insertion=0): '''Get single table row data (all columns for one domain).''' ret = [] for col in self.columns: - if self.tree_sorted and col.ls_head == 'NAME': + if (self.tree_sorted or self.ttree_sorted) and col.ls_head == 'NAME': ret.append(col.cell(vm, insertion)) else: ret.append(col.cell(vm)) @@ -474,6 +475,43 @@ def sort_to_tree(self, domains): return tree + def sort_to_ttree(self, domains): + '''Sort domains as a template tree. It returns a list of tuples. Each + tuple stores the level of the tree and the vm object. + + :param list() domains: The domains which will be sorted + :return list(tuple()) tree: returns a list of tuple(level, vm) + ''' + tree=[] + for dom in sorted(domains): + try: + if not dom.template: + tree.append((0,dom)) + domains.remove(dom) + except AttributeError: + tree.append((0,dom)) + domains.remove(dom) + + level = 0 + while len(domains) > 0: + last_len = len(domains) + for dom in sorted(domains): + try: + # Insert before (index of the template entry) + 1, + # i.e. after parent. What if parent entry is the last entry? + tree.insert( + tree.index((level,dom.template))+1, + (level+1,dom)) + domains.remove(dom) + # Pass if parent isn't in the tree yet + except ValueError: + pass + if len(domains) >= last_len: + raise Exception("sort_to_ttree: While loop is stuck!") + level += 1 + + return tree + def write_table(self, stream=sys.stdout): '''Sort & write whole table to file-like object. @@ -500,6 +538,10 @@ def sort_numeric(field: str) -> int: insertion_vm_list = self.sort_to_tree(self.domains) for insertion, vm in insertion_vm_list: table_data.append(self.get_row(vm, insertion)) + elif self.ttree_sorted: + insertion_vm_list = self.sort_to_ttree(self.domains) + for insertion, vm in insertion_vm_list: + table_data.append(self.get_row(vm, insertion)) else: for vm in sorted(self.domains): try: @@ -651,6 +693,10 @@ def get_parser(): action='store_const', const='tree', help='sort domain list as network tree') + parser.add_argument('--ttree', '-T', + action='store_const', const='ttree', + help='sort domain list as template tree') + parser_format.add_argument('--raw-data', action='store_true', help='Display specify data of specified VMs. Intended for ' 'bash-parsing.') @@ -906,7 +952,7 @@ def main(args=None, app=None): if matches_power_states(d, **pwrstates)] table = Table(domains=domains, colnames=columns, spinner=spinner, - raw_data=args.raw_data, tree_sorted=args.tree, + raw_data=args.raw_data, tree_sorted=args.tree, ttree_sorted=args.tree, sort_order=args.sort.upper(), reverse_sort=args.reverse, ignore_case=args.ignore_case) table.write_table(sys.stdout)