[prev in list] [next in list] [prev in thread] [next in thread] 

List:       qubes-devel
Subject:    Re: [qubes-devel] Qubes internal network topology
From:       Andrew <kyboren () riseup ! net>
Date:       2016-02-11 13:56:11
Message-ID: 56BC92FB.2070706 () riseup ! net
[Download RAW message or body]

Marek Marczykowski-Górecki:
> On Thu, Feb 11, 2016 at 12:20:40PM +0100, Zrubi wrote:
> > Hi,
> 
> > I want to visualize the internal Qubes network topology.
> > Is there any better way to get the actual topology than parsing the
> > output of `qvm-ls -n`
> 
> Unfortunately no, at least not yet. We'd love to have something like
> that in Qubes Manager in Qubes 4.0, but that's just a wishlist entry
> for now...
> 
> 

I've made my own hacky script for visualizing network and PCI
attachments.  Maybe this will tide you over until there's something more
official?

It's not completely bug-free; there is a spacing issue for ProxyVMs, but
visually I think it looks just fine and haven't had much motivation to
fix it.

Pic with redactions attached.

Andrew

-- 
You received this message because you are subscribed to the Google Groups \
"qubes-devel" group. To unsubscribe from this group and stop receiving emails from \
it, send an email to qubes-devel+unsubscribe@googlegroups.com. To post to this group, \
send email to qubes-devel@googlegroups.com. To view this discussion on the web visit \
https://groups.google.com/d/msgid/qubes-devel/56BC92FB.2070706%40riseup.net. For more \
options, visit https://groups.google.com/d/optout.


["print_vm_stats.png" (image/png)]
["print_vm_stats.py" (text/x-python)]

#!/usr/bin/python2
# -*- coding: utf-8
import ipdb
import subprocess
import time
import sys
import re
import os

from qubes.qubes import QubesVmCollection
from qubes.qubes import QubesHost

vm_info_table = {}
pci_dev_names = {}
total_mem = 0
total_cpu = 0
qhost = QubesHost()

conky = False
conky_color = '#ffffff'
color_output = True
forever = True
outfd = sys.stdout
out_to_file = False

nonterminal = '├'
terminal = '└'
extender = '─'
ghost = '┊'
color_ghost = True

def vm_net_depth(name):
    info = vm_info_table[name]
    return 1 + len(info['pci_devs']) + sum(map(lambda n : (vm_net_depth(n) if \
visible(n) else 0), info['child_vms']))

def visible(name):
    info = vm_info_table[name]
    if(info['running'] == True):
        return True
    elif(True in map(lambda n : visible(n), info['child_vms'])):
        return True
    else:
        return False

def print_vm_info(name, nindent, parent_color, parent_connector, last_child):
    global conky
    global conky_color
    global color_output
    global forever
    global outfd
    global total_mem
    global total_cpu

    if(visible(name) == False):
        return

    cpu = vm_info_table[name]['cpu_usage']
    memory = vm_info_table[name]['memory']
    pcidevs = vm_info_table[name]['pci_devs']
    child_vms = vm_info_table[name]['child_vms']
    color = vm_info_table[name]['color']
    running = vm_info_table[name]['running']

    total_mem = total_mem + memory
    total_cpu = total_cpu + cpu

    colorreset = '\x1b[0m'

    if(conky == True):
        color = '${{color {:s}}}'.format(vm_info_table[name]['color'])
        colorreset = '${{color {:s}}}'.format(conky_color)

    if(color_output == False):
        color = ''
        colorreset = ''
        parent_color = ''

    connector = ''
    inheritance = ''
    displayname = name

    if(running == False):
        displayname = '{:s}(*)'.format(name)


    if(nindent > 0):
        last_child_connector = ''
        not_last_child_connector = ''
        if(color_ghost == True):
            not_last_child_connector = '{:s}{:s}{:s} '.format(parent_color, ghost, \
colorreset)  else:
            not_last_child_connector = '{:s}{:s}{:s} '.format('', ghost, '')

        if(last_child):
            a = parent_connector + last_child_connector
            inheritance = a + '{trash:<{width}}'.format(trash = '', width = nindent/2 \
                + 1)
            connector = '{:s}{:s}{:s}{:s}{:s}'.format(a, parent_color, terminal, \
extender, colorreset)  else:
            a = parent_connector + '{:s}{:s}{:s}'.format(parent_color, nonterminal, \
colorreset)  inheritance = parent_connector + not_last_child_connector
            connector = '{:s}{:s}{:s}{:s}'.format(a, parent_color, extender, \
colorreset)

    outfd.write('{:5d}M {:5.1f}% {:s}{:s}{:s}{:s}\n'.format(memory, cpu, connector, \
color, displayname, colorreset))

    if(len(child_vms) != 0 or len(pcidevs) != 0):
        corner = ''
        if(len(child_vms) != 0):
            corner = nonterminal
        else:
            corner = terminal
        indent = '{:s}{:s}{:s}{:s}{:s}'.format(inheritance, color, corner, extender, \
colorreset);  if(len(pcidevs) != 0):
            for dev in pcidevs:
                extraspaces = 14 #for the stats we don't show on these lines
                padding = '{trash:<{width}}'.format(trash = '', width = extraspaces)
                outfd.write('{:s}{:s}{:s} ({:s})\n'.format(padding, indent, dev, \
pci_dev_names[dev]))  if(len(child_vms) != 0):
            sorted_child_vms = sorted(child_vms, key = lambda vm: vm_net_depth(vm))
            sorted_child_vms = filter(lambda n: visible(n), sorted_child_vms)
            i = 0
            while(i < len(sorted_child_vms)):
                child_vm = sorted_child_vms[i]
                child_is_last = False
                if(i == len(sorted_child_vms) - 1):
                    child_is_last = True
                print_vm_info(child_vm, nindent + 2, color, inheritance, \
child_is_last)  i = i+1

def print_vm_stats():
    global vm_info_table
    global out_to_file
    global total_mem
    global total_cpu

    qvm_collection = QubesVmCollection()
    qvm_collection.lock_db_for_reading()
    qvm_collection.load()
    qvm_collection.unlock_db()

    #get CPU usage (or try to, anyway) -- FIXME why does this take a whole fucking \
second?!  qvm_collection.popitem() #remove Dom0 from collection
    (cur_time, cpu_usages) = qhost.measure_cpu_usage(qvm_collection)

    #get various stats per VM        
    for v in qvm_collection.values():
        info_table = {}
        #ipd.asd
        name = v.name
        color = v.label.name
        if(conky == False):
            if color == 'black': color = '\x1b[38;5;7m\x1b[48;5;232m'
            elif color == 'gray': color = '\x1b[38;5;232m\x1b[48;5;7m'
            elif color == 'purple': color = '\x1b[38;5;232m\x1b[48;5;13m'
            elif color == 'blue': color = '\x1b[38;5;232m\x1b[48;5;4m'
            elif color == 'green': color = '\x1b[38;5;232m\x1b[48;5;46m'
            elif color == 'yellow': color = '\x1b[38;5;232m\x1b[48;5;226m'
            elif color == 'orange': color = '\x1b[38;5;232m\x1b[48;5;3m'
            elif color == 'red': color = '\x1b[38;5;232m\x1b[48;5;160m'
        else:
            if color == 'black': color = '#333333' #so it isn't completely black
            elif color == 'gray': color = '#777975'
            elif color == 'purple': color = '#75507b'
            elif color == 'blue': color = '#3465a4'
            elif color == 'green': color = '#73d216'
            elif color == 'yellow': color = '#edd400'
            elif color == 'orange': color = '#f57900'
            elif color == 'red': color = '#cc0000'

        if(v.xid != -1):
            info_table['running'] = True
            info_table['pci_devs'] = v.pcidevs
            info_table['cpu_usage'] = round(cpu_usages[v.get_xid()]['cpu_usage'],1)
        else:
            info_table['running'] = False
            info_table['pci_devs'] = []
            info_table['cpu_usage'] = 0

        info_table['color'] = color
        info_table['memory'] = v.get_mem() / 1024

        info_table['netvm'] = ''
        if(v.netvm): info_table['netvm'] = v.netvm.name

        info_table['type'] = ''
        if(v.type == 'ProxyVM'): info_table['type'] = 'ProxyVM'
        if(v.type == 'NetVM'): info_table['type'] = 'NetVM'

        info_table['child_vms'] = []
        vm_info_table[name] = info_table

    #iterate through the table and set the 'child_vms' field
    for name, info_table in vm_info_table.iteritems():
        if(info_table['netvm'] != ''):
            my_netvm = info_table['netvm']
            vm_info_table[my_netvm]['child_vms'].append(name)

    #sort VMs by NetVM depth
    defer = []
    for name, info in vm_info_table.iteritems():
        if(info['netvm'] == ''):
            defer.append(name)

    info_order = sorted(defer, key = lambda vm: vm_net_depth(vm));

    #clear the screen:
    if(forever == True and out_to_file == False):
        sys.stdout.write('\x1b[0;0H\x1b[2J')

    #clear system stats before re-scan
    total_mem = 0
    total_cpu = 0

    #print VM stats
    for vm in info_order:
        print_vm_info(vm, 0, '', '', True);

    print_system_stats();
    vm_info_table = {};

def get_dom0_cpu():
    global total_cpu

    stat = subprocess.Popen(['cat', '/proc/stat'], stdout = subprocess.PIPE)
    for line in stat.stdout:
        fields = line.split()
        if 'cpu' not in fields[0]: continue
        if fields[0] != 'cpu': continue
        user = float(fields[1])
        #usernice = float(fields[2])
        usernice = 0
        system = float(fields[3])
        idle = float(fields[4])
        total = ((user + usernice + system) / (user + usernice + system + idle)) * \
100  total_cpu = total_cpu + total
        #sys.stdout.write('found {:f} usage\n'.format(total))

def print_system_stats():
    global total_mem
    global total_cpu
    connector = ''
    color = '\x1b[38;5;7m\x1b[48;5;232m'
    colorreset = '\x1b[0m'
    get_dom0_cpu()
    outfd.write('\n{:s}{:05d}M {:5.1f}% {:s}{:s}{:s}\n'.format(color, total_mem, \
total_cpu, connector, 'SYSTEM TOTAL', colorreset))  return

def main():
    global conky_color
    global conky
    global color_output
    global forever
    global outfd
    global out_to_file

    outfile = ''
    out_to_file = False
    delay = 2

    #build PCI dev name dict
    cmd = subprocess.Popen(['/usr/sbin/lspci'], stdout = subprocess.PIPE)
    regexp = re.compile('([^:]*:[^.]*\.[^ ]*) ([^:]*):.*')
    for line in cmd.stdout:
        parts = regexp.match(line)
        pci_dev_names[parts.group(1)] = parts.group(2)

    #parse argv[]
    i = 0
    while(i < len(sys.argv)):
        arg = sys.argv[i]
        if(arg == 'color'):
            color_output = True
        elif(arg == 'nocolor'):
            color_output = False
        elif(arg == 'once'):
            forever = False
        elif(arg == 'forever'):
            forever = True
        elif(arg == 'conky'):
            conky = True
            i = i+1
            conky_color = sys.argv[i]
        elif(arg == 'outfile'):
            i = i+1
            outfile = sys.argv[i]
            out_to_file = True
            tmpfile = '/tmp/qubes-vm-stats'
        elif(arg == 'delay'):
            i = i+1
            delay = int(sys.argv[i]) - 1 #-1 because CPU usage takes ~1 second to \
get...  i = i+1

    if(forever == True):
        while True:
            try:
                if(out_to_file == True):
                    outfd = open(tmpfile, 'w')

                print_vm_stats()

                if(out_to_file == True):
                    outfd.close()
                    os.rename(tmpfile, outfile)
            except:
                    pass

            time.sleep(delay)
    else:
        print_vm_stats()

main()



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic