#!/usr/bin/python2 # -*- encoding: utf8 -*- # # The Qubes OS Project, http://www.qubes-os.org # # Copyright (C) 2010 Joanna Rutkowska # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # from qubes.qubes import QubesVmCollection from qubes.qubes import QubesHost from qubes.qubes import QubesException from argparse import ArgumentParser import sys fields = { "qid": {"func": "vm.qid"}, "name": {"func": "('=>' if qvm_collection.get_default_template() is not None\ and vm.qid == qvm_collection.get_default_template().qid else '')\ + ('[' if vm.is_template() else '')\ + ('<' if vm.is_disposablevm() else '')\ + ('{' if vm.is_netvm() else '')\ + vm.name \ + (']' if vm.is_template() else '')\ + ('>' if vm.is_disposablevm() else '')\ + ('}' if vm.is_netvm() else '')"}, "name-raw": {"func": "vm.name"}, "type": {"func": "'HVM' if vm.type == 'HVM' else \ ('Tpl' if vm.is_template() else \ ('' if vm.type in ['AppVM', 'DisposableVM'] else \ vm.type.replace('VM','')))"}, "updbl" : {"func": "'Yes' if vm.updateable else ''"}, "template": {"func": "'n/a' if vm.is_template() else\ ('None' if vm.template is None else\ vm.template.name)"}, "netvm": {"func": "'n/a' if vm.is_netvm() and not vm.is_proxyvm() else\ ('*' if vm.uses_default_netvm else '') +\ qvm_collection[vm.netvm.qid].name\ if vm.netvm is not None else '-'"}, "ip" : {"func": "vm.ip"}, "ip back" : {"func": "vm.gateway if vm.is_netvm() else 'n/a'"}, "gateway/DNS" : {"func": "vm.netvm.gateway if vm.netvm else 'n/a'"}, "xid" : {"func" : "vm.get_xid() if vm.is_running() else '-'"}, "mem" : {"func" : "(str(vm.get_mem()/1024) + ' MB') if vm.is_running() else '-'"}, "cpu" : {"func" : "round (cpu_usages[vm.get_xid()]['cpu_usage'], 1) if vm.is_running() else '-'"}, "disk": {"func" : "str(vm.get_disk_utilization()/(1024*1024)) + ' MB'"}, "state": {"func" : "vm.get_power_state()"}, "priv-curr": {"func" : "str(vm.get_disk_utilization_private_img()/(1024*1024)) + ' MB'"}, "priv-max": {"func" : "str(vm.get_private_img_sz()/(1024*1024)) + ' MB'"}, "priv-util": {"func" : "str(vm.get_disk_utilization_private_img()*100/vm.get_private_img_sz()) + '%' if vm.get_private_img_sz() != 0 else '-'"}, "root-curr": {"func" : "str(vm.get_disk_utilization_root_img()/(1024*1024)) + ' MB'"}, "root-max": {"func" : "str(vm.get_root_img_sz()/(1024*1024)) + ' MB'"}, "root-util": {"func" : "str(vm.get_disk_utilization_root_img()*100/vm.get_root_img_sz()) + '%' if vm.get_root_img_sz() != 0 else '-'"}, "label" : {"func" : "vm.label.name"}, "kernel" : {"func" : "('*' if vm.uses_default_kernel else '') + str(vm.kernel) if hasattr(vm, 'kernel') else 'n/a'"}, "kernelopts" : {"func" : "('*' if vm.uses_default_kernelopts else '') + str(vm.kernelopts) if hasattr(vm, 'kernelopts') else 'n/a'"}, "on" : {"func" : "'*' if vm.is_running() else ''"}, "last backup" : {"func": "str(vm.backup_timestamp.date()) if " "vm.backup_timestamp else '-'"}, } def main(): usage = "%(prog)s [options]" parser = ArgumentParser () parser.add_argument ("VMs", action="store", nargs="*", help="Specify VMs to be queried") parser.add_argument ("-n", "--network", dest="network", action="store_true", default=False, help="Show network addresses assigned to VMs") parser.add_argument ("-c", "--cpu", dest="cpu", action="store_true", default=False, help="Show CPU load") parser.add_argument ("-m", "--mem", dest="mem", action="store_true", default=False, help="Show memory usage") parser.add_argument ("-d", "--disk", dest="disk", action="store_true", default=False, help="Show VM disk utilization statistics") parser.add_argument ("-k", "--kernel", dest="kernel", action="store_true", default=False, help="Show VM kernel arguments") parser.add_argument ("-i", "--ids", dest="ids", action="store_true", default=False, help="Show Qubes and Xen id#s") parser.add_argument("-b", "--last-backup", dest="backup", action="store_true", default=False, help="Show date of last VM backup") parser.add_argument("--raw-list", dest="raw_list", action="store_true", default=False, help="List only VM names one per line") parser.add_argument("--raw-data", dest="raw_data", action="store", nargs="+", help="Display specify data of specified VMs.\ Intended for bash-parsing.") parser.add_argument("--list-fields", dest="list_fields", action="store_true", default=False, help="List field names valid for --raw-data") arguments = parser.parse_args () if arguments.list_fields: print '\n'.join(sorted(fields.keys())) return qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_reading() qvm_collection.load() qvm_collection.unlock_db() if arguments.raw_list: for vm in qvm_collection.values(): print vm.name return cpu_usages = None if arguments.raw_data: fields_to_display = arguments.raw_data if 'cpu' in arguments.raw_data: qhost = QubesHost() (measure_time, cpu_usages) = qhost.measure_cpu_usage(qvm_collection) else: fields_to_display = ["name", "on", "state", "updbl", "type", "template", "netvm", "label" ] if (arguments.ids): fields_to_display += ["qid", "xid"] if (arguments.cpu): qhost = QubesHost() (measure_time, cpu_usages) = qhost.measure_cpu_usage(qvm_collection) fields_to_display += ["cpu"] if (arguments.mem): fields_to_display += ["mem"] if arguments.backup: fields_to_display += ["last backup"] if (arguments.network): if 'template' in fields_to_display: fields_to_display.remove ("template") fields_to_display += ["ip", "ip back", "gateway/DNS"] if (arguments.disk): if 'template' in fields_to_display: fields_to_display.remove ("template") if 'netvm' in fields_to_display: fields_to_display.remove ("netvm") fields_to_display += ["priv-curr", "priv-max", "root-curr", "root-max", "disk" ] if (arguments.kernel): fields_to_display += ["kernel", "kernelopts" ] vms_list = [vm for vm in qvm_collection.values()] #assume VMs are presented in desired order: if len(arguments.VMs) > 0: vms_to_display = [vm for vm in vms_list if vm.name in arguments.VMs] #otherwise, format them accordingly: else: no_vms = len (vms_list) vms_to_display = [] # Frist, the NetVMs... for netvm in vms_list: if netvm.is_netvm(): vms_to_display.append (netvm) # Now, the AppVMs without template (or with template not included in the list)... for appvm in vms_list: if appvm.is_appvm() and not appvm.is_template() and \ (appvm.template is None or appvm.template not in vms_list): vms_to_display.append (appvm) # Now, the template, and all its AppVMs... for tvm in vms_list: if tvm.is_template(): vms_to_display.append (tvm) for vm in vms_list: if (vm.is_appvm() or vm.is_disposablevm()) and \ vm.template and vm.template.qid == tvm.qid: vms_to_display.append(vm) assert len(vms_to_display) == no_vms #We DON'T NEED a max_width if we devide output by pipes! # First calculate the maximum width of each field we want to display # also collect data to display for f in fields_to_display: fields[f]["max_width"] = len(f) data_to_display = [] for vm in vms_to_display: data_row = {} for f in fields_to_display: if vm.qid == 0 and (f.startswith('priv-') or f.startswith('root-') or f == 'disk'): data_row[f] = 'n/a' else: data_row[f] = str(eval(fields[f]["func"])) l = len(data_row[f]) if 'max_width' in fields[f] and l > fields[f]["max_width"]: fields[f]["max_width"] = l data_to_display.append(data_row) try: vm.verify_files() except QubesException as err: print >> sys.stderr, "WARNING: VM '{0}' has corrupted files!".format(vm.name) #Nicely formatted header only needed for humans if not arguments.raw_data: # Display the header s = "" for f in fields_to_display: fmt="{{0:-^{0}}}-+".format(fields[f]["max_width"] + 1) s += fmt.format('-') print s s = "" for f in fields_to_display: fmt="{{0:>{0}}} |".format(fields[f]["max_width"] + 1) s += fmt.format(f) print s s = "" for f in fields_to_display: fmt="{{0:-^{0}}}-+".format(fields[f]["max_width"] + 1) s += fmt.format('-') print s # ... and the actual data for row in data_to_display: s = "" for f in fields_to_display: fmt="{{0:>{0}}} |".format(fields[f]["max_width"] + 1) s += fmt.format(row[f]) print s #won't look pretty, but is easy to parse! else: for row in data_to_display: print '|'.join([row[f] for f in fields_to_display]) main()