ia64/xen-unstable

view tools/python/scripts/xapi.py @ 12758:5d0efb6f3983

[XENAPI] Add extra param for VM.start and fix case for printing out
the output of VM.get_record.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Fri Dec 01 17:31:30 2006 +0000 (2006-12-01)
parents 96621d417bd4
children b37e66b08bf5 98413fa7826c
line source
1 #!/usr/bin/python
2 #============================================================================
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of version 2.1 of the GNU Lesser General Public
5 # License as published by the Free Software Foundation.
6 #
7 # This library is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 # Lesser General Public License for more details.
11 #
12 # You should have received a copy of the GNU Lesser General Public
13 # License along with this library; if not, write to the Free Software
14 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 #============================================================================
16 # Copyright (C) 2006 XenSource Ltd.
17 #============================================================================
19 import sys
20 sys.path.append('/usr/lib/python')
22 from xen.util.xmlrpclib2 import ServerProxy
23 from optparse import *
24 from pprint import pprint
25 from types import DictType
26 from getpass import getpass
29 MB = 1024 * 1024
31 HOST_INFO_FORMAT = '%-20s: %-50s'
32 VM_LIST_FORMAT = '%(name_label)-18s %(memory_actual)-5s %(VCPUs_number)-5s'\
33 ' %(power_state)-10s %(uuid)-36s'
34 SR_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(physical_size)-10s' \
35 '%(type)-10s'
36 VDI_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(virtual_size)-8s '\
37 '%(sector_size)-8s'
38 VBD_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(VDI)-8s '\
39 '%(image)-8s'
41 COMMANDS = {
42 'host-info': ('', 'Get Xen Host Info'),
43 'host-set-name': ('', 'Set host name'),
44 'sr-list': ('', 'List all SRs'),
45 'vbd-list': ('', 'List all VBDs'),
46 'vbd-create': ('<domname> <pycfg> [opts]',
47 'Create VBD attached to domname'),
48 'vdi-create': ('<pycfg> [opts]', 'Create a VDI'),
49 'vdi-list' : ('', 'List all VDI'),
50 'vdi-rename': ('<vdi_uuid> <new_name>', 'Rename VDI'),
51 'vdi-delete': ('<vdi_uuid>', 'Delete VDI'),
52 'vif-create': ('<domname> <pycfg>', 'Create VIF attached to domname'),
53 'vtpm-create' : ('<domname> <pycfg>', 'Create VTPM attached to domname'),
55 'vm-create': ('<pycfg>', 'Create VM with python config'),
56 'vm-destroy': ('<domname>', 'Delete VM'),
58 'vm-list': ('[--long]', 'List all domains.'),
59 'vm-name': ('<uuid>', 'Name of UUID.'),
60 'vm-shutdown': ('<name> [opts]', 'Shutdown VM with name'),
61 'vm-start': ('<name>', 'Start VM with name'),
62 'vm-uuid': ('<name>', 'UUID of a domain by name.'),
63 }
65 OPTIONS = {
66 'vm-list': [(('-l', '--long'),
67 {'action':'store_true',
68 'help':'List all properties of VMs'})
69 ],
70 'vm-shutdown': [(('-f', '--force'), {'help': 'Shutdown Forcefully',
71 'action': 'store_true'})],
73 'vdi-create': [(('--name-label',), {'help': 'Name for VDI'}),
74 (('--description',), {'help': 'Description for VDI'}),
75 (('--sector-size',), {'type': 'int',
76 'help': 'Sector size'}),
77 (('--virtual-size',), {'type': 'int',
78 'help': 'Size of VDI in sectors'}),
79 (('--type',), {'choices': ['system', 'user', 'ephemeral'],
80 'help': 'VDI type'}),
81 (('--sharable',), {'action': 'store_true',
82 'help': 'VDI sharable'}),
83 (('--read-only',), {'action': 'store_true',
84 'help': 'Read only'})],
86 'vbd-create': [(('--VDI',), {'help': 'UUID of VDI to attach to.'}),
87 (('--mode',), {'choices': ['RO', 'RW'],
88 'help': 'device mount mode'}),
89 (('--driver',), {'choices':['paravirtualised', 'ioemu'],
90 'help': 'Driver for VBD'}),
91 (('--device',), {'help': 'Device name on guest domain'}),
92 (('--image',), {'help': 'Location of drive image.'})]
94 }
96 class OptionError(Exception):
97 pass
99 class XenAPIError(Exception):
100 pass
102 #
103 # Extra utility functions
104 #
106 class IterableValues(Values):
107 """Better interface to the list of values from optparse."""
109 def __iter__(self):
110 for opt, val in self.__dict__.items():
111 if opt[0] == '_' or callable(val):
112 continue
113 yield opt, val
116 def parse_args(cmd_name, args, set_defaults = False):
117 argstring, desc = COMMANDS[cmd_name]
118 parser = OptionParser(usage = 'xapi %s %s' % (cmd_name, argstring),
119 description = desc)
120 if cmd_name in OPTIONS:
121 for optargs, optkwds in OPTIONS[cmd_name]:
122 parser.add_option(*optargs, **optkwds)
124 if set_defaults:
125 default_values = parser.get_default_values()
126 defaults = IterableValues(default_values.__dict__)
127 else:
128 defaults = IterableValues()
129 (opts, extraargs) = parser.parse_args(args = list(args),
130 values = defaults)
131 return opts, extraargs
133 def execute(fn, *args):
134 result = fn(*args)
135 if type(result) != DictType:
136 raise TypeError("Function returned object of type: %s" %
137 str(type(result)))
138 if 'Value' not in result:
139 raise XenAPIError(*result['ErrorDescription'])
140 return result['Value']
142 _initialised = False
143 _server = None
144 _session = None
145 def _connect(*args):
146 global _server, _session, _initialised
147 if not _initialised:
148 _server = ServerProxy('httpu:///var/run/xend/xmlrpc.sock')
149 login = raw_input("Login: ")
150 password = getpass()
151 creds = (login, password)
152 _session = execute(_server.session.login_with_password, *creds)
153 _initialised = True
154 return (_server, _session)
156 def _stringify(adict):
157 return dict([(k, str(v)) for k, v in adict.items()])
159 def _read_python_cfg(filename):
160 cfg = {}
161 execfile(filename, {}, cfg)
162 return cfg
164 def resolve_vm(server, session, vm_name):
165 vm_uuid = execute(server.VM.get_by_name_label, session, vm_name)
166 if not vm_uuid:
167 return None
168 else:
169 return vm_uuid[0]
171 def resolve_vdi(server, session, vdi_name):
172 vdi_uuid = execute(server.VDI.get_by_name_label, session, vdi_name)
173 if not vdi_uuid:
174 return None
175 else:
176 return vdi_uuid[0]
178 #
179 # Actual commands
180 #
182 def xapi_host_info(*args):
183 server, session = _connect()
184 hosts = execute(server.host.get_all, session)
185 for host in hosts: # there is only one, but ..
186 hostinfo = execute(server.host.get_record, session, host)
187 print HOST_INFO_FORMAT % ('Name', hostinfo['name_label'])
188 print HOST_INFO_FORMAT % ('Version', hostinfo['software_version'])
189 print HOST_INFO_FORMAT % ('CPUs', len(hostinfo['host_CPUs']))
190 print HOST_INFO_FORMAT % ('VMs', len(hostinfo['resident_VMs']))
191 print HOST_INFO_FORMAT % ('UUID', host)
193 def xapi_host_set_name(*args):
194 if len(args) < 1:
195 raise OptionError("No hostname specified")
197 server, session = _connect()
198 hosts = execute(server.host.get_all, session)
199 if len(hosts) > 0:
200 execute(server.host.set_name_label, session, hosts[0], args[0])
201 print 'Hostname: %s' % execute(server.host.get_name_label, session,
202 hosts[0])
204 def xapi_vm_uuid(*args):
205 if len(args) < 1:
206 raise OptionError("No domain name specified")
208 server, session = _connect()
209 vm_uuid = resolve_vm(server, session, args[0])
210 print vm_uuid
212 def xapi_vm_name(*args):
213 if len(args) < 1:
214 raise OptionError("No UUID specified")
216 server, session = _connect()
217 vm_name = execute(server.VM.get_name_label, session, args[0])
218 print vm_name
220 def xapi_vm_list(*args):
221 opts, args = parse_args('vm-list', args, set_defaults = True)
222 is_long = opts and opts.long
224 server, session = _connect()
225 vm_uuids = execute(server.VM.get_all, session)
226 if not is_long:
227 print VM_LIST_FORMAT % {'name_label':'Name',
228 'memory_actual':'Mem',
229 'VCPUs_number': 'VCPUs',
230 'power_state': 'State',
231 'uuid': 'UUID'}
233 for uuid in vm_uuids:
234 vm_info = execute(server.VM.get_record, session, uuid)
235 if is_long:
236 vbds = vm_info['VBDs']
237 vifs = vm_info['VIFs']
238 vtpms = vm_info['VTPMs']
239 vif_infos = []
240 vbd_infos = []
241 vtpm_infos = []
242 for vbd in vbds:
243 vbd_info = execute(server.VBD.get_record, session, vbd)
244 vbd_infos.append(vbd_info)
245 for vif in vifs:
246 vif_info = execute(server.VIF.get_record, session, vif)
247 vif_infos.append(vif_info)
248 for vtpm in vtpms:
249 vtpm_info = execute(server.VTPM.get_record, session, vtpm)
250 vtpm_infos.append(vtpm_info)
251 vm_info['VBDs'] = vbd_infos
252 vm_info['VIFs'] = vif_infos
253 vm_info['VTPMs'] = vtpm_infos
254 pprint(vm_info)
255 else:
256 print VM_LIST_FORMAT % _stringify(vm_info)
258 def xapi_vm_create(*args):
259 if len(args) < 1:
260 raise OptionError("Configuration file not specified")
262 filename = args[0]
263 cfg = _read_python_cfg(filename)
265 print 'Creating VM from %s ..' % filename
266 server, session = _connect()
267 uuid = execute(server.VM.create, session, cfg)
268 print 'Done. (%s)' % uuid
269 print uuid
271 def xapi_vm_destroy(*args):
272 if len(args) < 1:
273 raise OptionError("No domain name specified.")
275 server, session = _connect()
276 vm_uuid = resolve_vm(server, session, args[0])
277 print 'Destroying VM %s (%s)' % (args[0], vm_uuid)
278 success = execute(server.VM.destroy, session, vm_uuid)
279 print 'Done.'
282 def xapi_vm_start(*args):
283 if len(args) < 1:
284 raise OptionError("No Domain name specified.")
286 server, session = _connect()
287 vm_uuid = resolve_vm(server, session, args[0])
288 print 'Starting VM %s (%s)' % (args[0], vm_uuid)
289 success = execute(server.VM.start, session, vm_uuid, False)
290 print 'Done.'
292 def xapi_vm_shutdown(*args):
293 opts, args = parse_args("vm-shutdown", args, set_defaults = True)
295 if len(args) < 1:
296 raise OptionError("No Domain name specified.")
298 server, session = _connect()
299 vm_uuid = resolve_vm(server, session, args[0])
300 if opts.force:
301 print 'Forcefully shutting down VM %s (%s)' % (args[0], vm_uuid)
302 success = execute(server.VM.hard_shutdown, session, vm_uuid)
303 else:
304 print 'Shutting down VM %s (%s)' % (args[0], vm_uuid)
305 success = execute(server.VM.clean_shutdown, session, vm_uuid)
306 print 'Done.'
308 def xapi_vbd_create(*args):
309 opts, args = parse_args('vbd-create', args)
311 if len(args) < 2:
312 raise OptionError("Configuration file and domain not specified")
314 domname = args[0]
316 if len(args) > 1:
317 filename = args[1]
318 cfg = _read_python_cfg(filename)
319 else:
320 cfg = {}
322 for opt, val in opts:
323 cfg[opt] = val
325 print 'Creating VBD ...',
326 server, session = _connect()
327 vm_uuid = resolve_vm(server, session, domname)
328 cfg['VM'] = vm_uuid
329 vbd_uuid = execute(server.VBD.create, session, cfg)
330 print 'Done. (%s)' % vbd_uuid
332 def xapi_vif_create(*args):
333 if len(args) < 2:
334 raise OptionError("Configuration file not specified")
336 domname = args[0]
337 filename = args[1]
338 cfg = _read_python_cfg(filename)
340 print 'Creating VIF from %s ..' % filename
341 server, session = _connect()
342 vm_uuid = resolve_vm(server, session, domname)
343 cfg['VM'] = vm_uuid
344 vif_uuid = execute(server.VIF.create, session, cfg)
345 print 'Done. (%s)' % vif_uuid
347 def xapi_vbd_list(*args):
348 server, session = _connect()
349 domname = args[0]
351 dom_uuid = resolve_vm(server, session, domname)
352 vbds = execute(server.VM.get_VBDs, session, dom_uuid)
354 print VBD_LIST_FORMAT % {'name_label': 'VDI Label',
355 'uuid' : 'UUID',
356 'VDI': 'VDI',
357 'image': 'Image'}
359 for vbd in vbds:
360 vbd_struct = execute(server.VBD.get_record, session, vbd)
361 print VBD_LIST_FORMAT % vbd_struct
363 def xapi_vdi_list(*args):
364 server, session = _connect()
365 vdis = execute(server.VDI.get_all, session)
367 print VDI_LIST_FORMAT % {'name_label': 'VDI Label',
368 'uuid' : 'UUID',
369 'virtual_size': 'Sectors',
370 'sector_size': 'Sector Size'}
372 for vdi in vdis:
373 vdi_struct = execute(server.VDI.get_record, session, vdi)
374 print VDI_LIST_FORMAT % vdi_struct
376 def xapi_sr_list(*args):
377 server, session = _connect()
378 srs = execute(server.SR.get_all, session)
379 print SR_LIST_FORMAT % {'name_label': 'SR Label',
380 'uuid' : 'UUID',
381 'physical_size': 'Size',
382 'type': 'Type'}
383 for sr in srs:
384 sr_struct = execute(server.SR.get_record, session, sr)
385 sr_struct['physical_size'] = int(sr_struct['physical_size'])/MB
386 print SR_LIST_FORMAT % sr_struct
388 def xapi_vdi_create(*args):
389 opts, args = parse_args('vdi-create', args)
391 if len(args) > 0:
392 cfg = _read_python_cfg(args[0])
393 else:
394 cfg = {}
396 for opt, val in opts:
397 cfg[opt] = val
399 server, session = _connect()
400 srs = execute(server.SR.get_all, session)
401 sr = srs[0]
402 cfg['SR'] = sr
404 size = (cfg['virtual_size'] * cfg['sector_size'])/MB
405 print 'Creating VDI of size: %dMB ..' % size,
406 uuid = execute(server.VDI.create, session, cfg)
407 print 'Done. (%s)' % uuid
409 def xapi_vdi_delete(*args):
410 server, session = _connect()
411 if len(args) < 1:
412 raise OptionError('Not enough arguments')
414 vdi_uuid = args[0]
415 print 'Deleting VDI %s' % vdi_uuid
416 result = execute(server.VDI.destroy, session, vdi_uuid)
417 print 'Done.'
419 def xapi_vdi_rename(*args):
420 server, session = _connect()
421 if len(args) < 2:
422 raise OptionError('Not enough arguments')
424 vdi_uuid = args[0]
425 vdi_name = args[1]
426 print 'Renaming VDI %s to %s' % (vdi_uuid, vdi_name)
427 result = execute(server.VDI.set_name_label, session, vdi_uuid, vdi_name)
428 print 'Done.'
431 def xapi_vtpm_create(*args):
432 server, session = _connect()
433 domname = args[0]
434 cfg = _read_python_cfg(args[1])
436 vm_uuid = resolve_vm(server, session, domname)
437 cfg['VM'] = vm_uuid
438 print "Creating vTPM with cfg = %s" % cfg
439 vtpm_uuid = execute(server.VTPM.create, session, cfg)
440 print "Done. (%s)" % vtpm_uuid
441 vtpm_id = execute(server.VTPM.get_instance, session, vtpm_uuid)
442 print "Has instance number '%s'" % vtpm_id
443 vtpm_be = execute(server.VTPM.get_backend, session, vtpm_uuid)
444 print "Has backend in '%s'" % vtpm_be
445 driver = execute(server.VTPM.get_driver, session, vtpm_uuid)
446 print "Has driver type '%s'" % driver
447 vtpm_rec = execute(server.VTPM.get_record, session, vtpm_uuid)
448 print "Has vtpm record '%s'" % vtpm_rec
449 vm = execute(server.VTPM.get_VM, session, vtpm_uuid)
450 print "Has VM '%s'" % vm
453 #
454 # Command Line Utils
455 #
456 import cmd
457 import shlex
459 class XenAPICmd(cmd.Cmd):
460 def __init__(self, server, session):
461 cmd.Cmd.__init__(self)
462 self.server = server
463 self.session = session
464 self.prompt = ">>> "
466 def default(self, line):
467 words = shlex.split(line)
468 if len(words) > 0:
469 cmd_name = words[0].replace('-', '_')
470 func_name = 'xapi_%s' % cmd_name
471 func = globals().get(func_name)
472 if func:
473 try:
474 args = tuple(words[1:])
475 func(*args)
476 return True
477 except SystemExit:
478 return False
479 except OptionError, e:
480 print 'Error:', str(e)
481 return False
482 except Exception, e:
483 import traceback
484 traceback.print_exc()
485 return False
486 print '*** Unknown command: %s' % words[0]
487 return False
489 def do_EOF(self, line):
490 print
491 sys.exit(0)
493 def do_help(self, line):
494 usage(print_usage = False)
496 def emptyline(self):
497 pass
499 def postcmd(self, stop, line):
500 return False
502 def precmd(self, line):
503 words = shlex.split(line)
504 if len(words) > 0:
505 words0 = words[0].replace('-', '_')
506 return ' '.join([words0] + words[1:])
507 else:
508 return line
510 def shell():
511 server, session = _connect()
512 x = XenAPICmd(server, session)
513 x.cmdloop('Xen API Prompt. Type "help" for a list of functions')
515 def usage(command = None, print_usage = True):
516 if not command:
517 if print_usage:
518 print 'Usage: xapi <subcommand> [options] [args]'
519 print
520 print 'Subcommands:'
521 print
522 sorted_commands = sorted(COMMANDS.keys())
523 for command in sorted_commands:
524 args, description = COMMANDS[command]
525 print '%-16s %-40s' % (command, description)
526 print
527 else:
528 parse_args(command, ['-h'])
530 def main(args):
532 if len(args) < 1 or args[0] in ('-h', '--help', 'help'):
533 usage()
534 sys.exit(1)
536 subcmd = args[0]
537 subcmd_func_name = 'xapi_' + subcmd.replace('-', '_')
538 subcmd_func = globals().get(subcmd_func_name, None)
540 if subcmd == 'shell':
541 shell()
542 elif not subcmd_func or not callable(subcmd_func):
543 print 'Error: Unable to find subcommand \'%s\'' % subcmd
544 usage()
545 sys.exit(1)
547 if '-h' in args[1:] or '--help' in args[1:]:
548 usage(subcmd)
549 sys.exit(1)
551 try:
552 subcmd_func(*args[1:])
553 except XenAPIError, e:
554 print 'Error: %s' % str(e.args[1])
555 sys.exit(2)
556 except OptionError, e:
557 print 'Error: %s' % e
559 sys.exit(0)
561 if __name__ == "__main__":
562 import sys
563 main(sys.argv[1:])