13d004a37SPhilippe Mathieu-Daudé#!/usr/bin/env python3 2dc2b651aSVladimir Sementsov-Ogievskiy# 3dc2b651aSVladimir Sementsov-Ogievskiy# Render Qemu Block Graph 4dc2b651aSVladimir Sementsov-Ogievskiy# 5dc2b651aSVladimir Sementsov-Ogievskiy# Copyright (c) 2018 Virtuozzo International GmbH. All rights reserved. 6dc2b651aSVladimir Sementsov-Ogievskiy# 7dc2b651aSVladimir Sementsov-Ogievskiy# This program is free software; you can redistribute it and/or modify 8dc2b651aSVladimir Sementsov-Ogievskiy# it under the terms of the GNU General Public License as published by 9dc2b651aSVladimir Sementsov-Ogievskiy# the Free Software Foundation; either version 2 of the License, or 10dc2b651aSVladimir Sementsov-Ogievskiy# (at your option) any later version. 11dc2b651aSVladimir Sementsov-Ogievskiy# 12dc2b651aSVladimir Sementsov-Ogievskiy# This program is distributed in the hope that it will be useful, 13dc2b651aSVladimir Sementsov-Ogievskiy# but WITHOUT ANY WARRANTY; without even the implied warranty of 14dc2b651aSVladimir Sementsov-Ogievskiy# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15dc2b651aSVladimir Sementsov-Ogievskiy# GNU General Public License for more details. 16dc2b651aSVladimir Sementsov-Ogievskiy# 17dc2b651aSVladimir Sementsov-Ogievskiy# You should have received a copy of the GNU General Public License 18dc2b651aSVladimir Sementsov-Ogievskiy# along with this program. If not, see <http://www.gnu.org/licenses/>. 19dc2b651aSVladimir Sementsov-Ogievskiy# 20dc2b651aSVladimir Sementsov-Ogievskiy 21dc2b651aSVladimir Sementsov-Ogievskiyimport os 22dc2b651aSVladimir Sementsov-Ogievskiyimport sys 23dc2b651aSVladimir Sementsov-Ogievskiyimport subprocess 24dc2b651aSVladimir Sementsov-Ogievskiyimport json 25dc2b651aSVladimir Sementsov-Ogievskiyfrom graphviz import Digraph 268f8fd9edSCleber Rosa 278f8fd9edSCleber Rosasys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python')) 2837094b6dSJohn Snowfrom qemu.qmp import QMPError 2937094b6dSJohn Snowfrom qemu.qmp.legacy import QEMUMonitorProtocol 30dc2b651aSVladimir Sementsov-Ogievskiy 31dc2b651aSVladimir Sementsov-Ogievskiy 32dc2b651aSVladimir Sementsov-Ogievskiydef perm(arr): 33dc2b651aSVladimir Sementsov-Ogievskiy s = 'w' if 'write' in arr else '_' 34dc2b651aSVladimir Sementsov-Ogievskiy s += 'r' if 'consistent-read' in arr else '_' 35dc2b651aSVladimir Sementsov-Ogievskiy s += 'u' if 'write-unchanged' in arr else '_' 36dc2b651aSVladimir Sementsov-Ogievskiy s += 's' if 'resize' in arr else '_' 37dc2b651aSVladimir Sementsov-Ogievskiy return s 38dc2b651aSVladimir Sementsov-Ogievskiy 39dc2b651aSVladimir Sementsov-Ogievskiy 40dc2b651aSVladimir Sementsov-Ogievskiydef render_block_graph(qmp, filename, format='png'): 41dc2b651aSVladimir Sementsov-Ogievskiy ''' 42dc2b651aSVladimir Sementsov-Ogievskiy Render graph in text (dot) representation into "@filename" and 43dc2b651aSVladimir Sementsov-Ogievskiy representation in @format into "@filename.@format" 44dc2b651aSVladimir Sementsov-Ogievskiy ''' 45dc2b651aSVladimir Sementsov-Ogievskiy 46*684750abSVladimir Sementsov-Ogievskiy bds_nodes = qmp.cmd('query-named-block-nodes') 47dc2b651aSVladimir Sementsov-Ogievskiy bds_nodes = {n['node-name']: n for n in bds_nodes} 48dc2b651aSVladimir Sementsov-Ogievskiy 49*684750abSVladimir Sementsov-Ogievskiy job_nodes = qmp.cmd('query-block-jobs') 50dc2b651aSVladimir Sementsov-Ogievskiy job_nodes = {n['device']: n for n in job_nodes} 51dc2b651aSVladimir Sementsov-Ogievskiy 52*684750abSVladimir Sementsov-Ogievskiy block_graph = qmp.cmd('x-debug-query-block-graph') 53dc2b651aSVladimir Sementsov-Ogievskiy 54dc2b651aSVladimir Sementsov-Ogievskiy graph = Digraph(comment='Block Nodes Graph') 55dc2b651aSVladimir Sementsov-Ogievskiy graph.format = format 56dc2b651aSVladimir Sementsov-Ogievskiy graph.node('permission symbols:\l' 57dc2b651aSVladimir Sementsov-Ogievskiy ' w - Write\l' 58dc2b651aSVladimir Sementsov-Ogievskiy ' r - consistent-Read\l' 59dc2b651aSVladimir Sementsov-Ogievskiy ' u - write - Unchanged\l' 60dc2b651aSVladimir Sementsov-Ogievskiy ' g - Graph-mod\l' 61dc2b651aSVladimir Sementsov-Ogievskiy ' s - reSize\l' 62dc2b651aSVladimir Sementsov-Ogievskiy 'edge label scheme:\l' 63dc2b651aSVladimir Sementsov-Ogievskiy ' <child type>\l' 64dc2b651aSVladimir Sementsov-Ogievskiy ' <perm>\l' 65dc2b651aSVladimir Sementsov-Ogievskiy ' <shared_perm>\l', shape='none') 66dc2b651aSVladimir Sementsov-Ogievskiy 67dc2b651aSVladimir Sementsov-Ogievskiy for n in block_graph['nodes']: 68dc2b651aSVladimir Sementsov-Ogievskiy if n['type'] == 'block-driver': 69dc2b651aSVladimir Sementsov-Ogievskiy info = bds_nodes[n['name']] 70dc2b651aSVladimir Sementsov-Ogievskiy label = n['name'] + ' [' + info['drv'] + ']' 71dc2b651aSVladimir Sementsov-Ogievskiy if info['drv'] == 'file': 72dc2b651aSVladimir Sementsov-Ogievskiy label += '\n' + os.path.basename(info['file']) 73dc2b651aSVladimir Sementsov-Ogievskiy shape = 'ellipse' 74dc2b651aSVladimir Sementsov-Ogievskiy elif n['type'] == 'block-job': 75dc2b651aSVladimir Sementsov-Ogievskiy info = job_nodes[n['name']] 76dc2b651aSVladimir Sementsov-Ogievskiy label = info['type'] + ' job (' + n['name'] + ')' 77dc2b651aSVladimir Sementsov-Ogievskiy shape = 'box' 78dc2b651aSVladimir Sementsov-Ogievskiy else: 79dc2b651aSVladimir Sementsov-Ogievskiy assert n['type'] == 'block-backend' 80dc2b651aSVladimir Sementsov-Ogievskiy label = n['name'] if n['name'] else 'unnamed blk' 81dc2b651aSVladimir Sementsov-Ogievskiy shape = 'box' 82dc2b651aSVladimir Sementsov-Ogievskiy 83dc2b651aSVladimir Sementsov-Ogievskiy graph.node(str(n['id']), label, shape=shape) 84dc2b651aSVladimir Sementsov-Ogievskiy 85dc2b651aSVladimir Sementsov-Ogievskiy for e in block_graph['edges']: 86dc2b651aSVladimir Sementsov-Ogievskiy label = '%s\l%s\l%s\l' % (e['name'], perm(e['perm']), 87dc2b651aSVladimir Sementsov-Ogievskiy perm(e['shared-perm'])) 88dc2b651aSVladimir Sementsov-Ogievskiy graph.edge(str(e['parent']), str(e['child']), label=label) 89dc2b651aSVladimir Sementsov-Ogievskiy 90dc2b651aSVladimir Sementsov-Ogievskiy graph.render(filename) 91dc2b651aSVladimir Sementsov-Ogievskiy 92dc2b651aSVladimir Sementsov-Ogievskiy 93dc2b651aSVladimir Sementsov-Ogievskiyclass LibvirtGuest(): 94dc2b651aSVladimir Sementsov-Ogievskiy def __init__(self, name): 95dc2b651aSVladimir Sementsov-Ogievskiy self.name = name 96dc2b651aSVladimir Sementsov-Ogievskiy 97*684750abSVladimir Sementsov-Ogievskiy def cmd(self, cmd): 98dc2b651aSVladimir Sementsov-Ogievskiy # only supports qmp commands without parameters 99dc2b651aSVladimir Sementsov-Ogievskiy m = {'execute': cmd} 100dc2b651aSVladimir Sementsov-Ogievskiy ar = ['virsh', 'qemu-monitor-command', self.name, json.dumps(m)] 101dc2b651aSVladimir Sementsov-Ogievskiy 102dc2b651aSVladimir Sementsov-Ogievskiy reply = json.loads(subprocess.check_output(ar)) 103dc2b651aSVladimir Sementsov-Ogievskiy 104dc2b651aSVladimir Sementsov-Ogievskiy if 'error' in reply: 10505908602SJohn Snow raise QMPError(reply) 106dc2b651aSVladimir Sementsov-Ogievskiy 107dc2b651aSVladimir Sementsov-Ogievskiy return reply['return'] 108dc2b651aSVladimir Sementsov-Ogievskiy 109dc2b651aSVladimir Sementsov-Ogievskiy 110dc2b651aSVladimir Sementsov-Ogievskiyif __name__ == '__main__': 111dc2b651aSVladimir Sementsov-Ogievskiy obj = sys.argv[1] 112dc2b651aSVladimir Sementsov-Ogievskiy out = sys.argv[2] 113dc2b651aSVladimir Sementsov-Ogievskiy 114dc2b651aSVladimir Sementsov-Ogievskiy if os.path.exists(obj): 115dc2b651aSVladimir Sementsov-Ogievskiy # assume unix socket 116dc2b651aSVladimir Sementsov-Ogievskiy qmp = QEMUMonitorProtocol(obj) 117dc2b651aSVladimir Sementsov-Ogievskiy qmp.connect() 118dc2b651aSVladimir Sementsov-Ogievskiy else: 119dc2b651aSVladimir Sementsov-Ogievskiy # assume libvirt guest name 120dc2b651aSVladimir Sementsov-Ogievskiy qmp = LibvirtGuest(obj) 121dc2b651aSVladimir Sementsov-Ogievskiy 122dc2b651aSVladimir Sementsov-Ogievskiy render_block_graph(qmp, out) 123