#!/usr/bin/python2 ''' virsh-xpath, version 0.1 Copyright 2012 Jan ONDREJ (SAL) 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. Syntax: virsh-xpath --guest NAME PARAMETERS [PARAMETERS ...] PARAMETERS syntax: "xpath selector" [--add element --attr atribute=value \\ |--delete \\ |--remove attribute [--remove attribute ...] \\ |[--update] --attr attribute=value \\ |--stdin \\ |--dry-run] Explanation: --stdin read more commands from stdin --dry-run do not change anything, just print out changed output --query only print query results and exist, do not modify document Examples: # Query boot options: --guest NAME --query //os/boot # Add an element with attributes: --guest NAME //os --add bios --attr useserial=yes # Update attribute of an element: --guest NAME //devices/disk/driver --update --attr cache=none # Remove attributes: --guest NAME //disk/driver --remove cache --remove type # Delete element: --guest NAME '//os/boot[@dev="hd"]' --delete To apply multiple commands at once, use --stdin parameter and send options from stdin. Example: virsh-xpath --guest NAME --stdin << EOF //os --add bios --attr useserial=yes //devices/disk/driver --update --attr cache=none EOF ''' import sys, os, getopt, libxml2 def guests(opts): for key, value in opts: if key=='--guest' or key=='-G': yield value def split_commands(args): ret = [] for arg in args: if arg.startswith("/"): yield ret ret = [arg] elif arg=="--stdin": if ret: yield ret ret = [] for line in sys.stdin.readlines(): yield line.strip().split(" ") else: ret.append(arg) if ret: yield ret def apply_opts(e, opts): for key, value in opts: if key=='--attribute' or key=='-t': k, v = value.split("=", 1) if v is None: e.unsetProp(k) else: e.setProp(k, v) return e def get_opts(opts): ret = [] for key, value in opts: if key=='--attribute' or key=='-t': k, v = value.split("=", 1) ret.append('%s="%s"' % (k, v)) return ' '.join(ret) if __name__ == "__main__": commands = list(split_commands(sys.argv[1:])) try: mopts, margs = getopt.gnu_getopt(commands[0], 'G:hQ', ['guest=', 'capabilities', 'help', 'stdin', 'dry-run', 'query'] ) mparams = dict(mopts) if '--help' in mparams or '-h' in mparams: print(__doc__) sys.exit(0) except getopt.GetoptError as err: print("Error:", str(err)) sys.exit(1) if '--capabilities' in mparams: source = os.popen('virsh capabilities').read() doc = libxml2.parseDoc(source) ctx = doc.xpathNewContext() query_args = ' '.join(sys.argv[2:]) elements = ctx.xpathEval(query_args) for element in elements: print(element) for guest in guests(mopts): source = os.popen('virsh dumpxml %s' % guest).read() doc = libxml2.parseDoc(source) ctx = doc.xpathNewContext() for argv in commands[1:]: try: opts, args = getopt.gnu_getopt(argv, 'a:dr:ut:', ['add=', 'delete', 'remove=', 'update', 'attribute='] ) params = dict(opts) except getopt.GetoptError as err: print("Error:", str(err)) sys.exit(1) query_args = ' '.join(args) elements = ctx.xpathEval(query_args) if '--add' in params or '-a' in params: if '-a' in params: node = params.get('-a') elif '--add' in params: node = params.get('--add') #e = pq("<%s/>\n" % add) e = libxml2.newNode(node) apply_opts(e, opts) for element in elements: element.addChild(e) elif '--delete' in params or '-d' in params: for element in elements: element.unlinkNode() elif '--remove' in params or '-r' in params: for key, value in opts: if key=='-r' or key=='--remove': for element in elements: element.unsetProp(value) elif opts: # '--update' in params or '-u' in params or "no params": for element in elements: apply_opts(element, opts) else: # only print query for element in elements: print(element) if '--query' not in mparams and '-Q' not in mparams: if '--dry-run' in mparams: print(doc.getRootElement()) else: f = os.popen('EDITOR="cat >" virsh edit %s' % guest, 'w') f.write(str(doc.getRootElement())) f.close() doc.freeDoc() ctx.xpathFreeContext()