VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxShell/vboxshell.py@ 20598

Last change on this file since 20598 was 20598, checked in by vboxsync, 16 years ago

Main: implmented waitForEvents(aTimeout) API for XPCOM targets, added command to VBox shell using this API

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.5 KB
Line 
1#!/usr/bin/python
2#
3# Copyright (C) 2009 Sun Microsystems, Inc.
4#
5# This file is part of VirtualBox Open Source Edition (OSE), as
6# available from http://www.virtualbox.org. This file is free software;
7# you can redistribute it and/or modify it under the terms of the GNU
8# General Public License (GPL) as published by the Free Software
9# Foundation, in version 2 as it comes in the "COPYING" file of the
10# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
11# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
12#
13# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
14# Clara, CA 95054 USA or visit http://www.sun.com if you need
15# additional information or have any questions.
16#
17#
18#################################################################################
19# This program is a simple interactive shell for VirtualBox. You can query #
20# information and issue commands from a simple command line. #
21# #
22# It also provides you with examples on how to use VirtualBox's Python API. #
23# This shell is even somewhat documented and supports TAB-completion and #
24# history if you have Python readline installed. #
25# #
26# Enjoy. #
27################################################################################
28
29import os,sys
30import traceback
31
32class PerfCollector:
33 """ This class provides a wrapper over IPerformanceCollector in order to
34 get more 'pythonic' interface.
35
36 To begin collection of metrics use setup() method.
37
38 To get collected data use query() method.
39
40 It is possible to disable metric collection without changing collection
41 parameters with disable() method. The enable() method resumes metric
42 collection.
43 """
44
45 def __init__(self, vb):
46 """ Initializes the instance.
47
48 Pass an instance of IVirtualBox as parameter.
49 """
50 self.collector = vb.performanceCollector
51
52 def setup(self, names, objects, period, nsamples):
53 """ Discards all previously collected values for the specified
54 metrics, sets the period of collection and the number of retained
55 samples, enables collection.
56 """
57 self.collector.setupMetrics(names, objects, period, nsamples)
58
59 def enable(self, names, objects):
60 """ Resumes metric collection for the specified metrics.
61 """
62 self.collector.enableMetrics(names, objects)
63
64 def disable(self, names, objects):
65 """ Suspends metric collection for the specified metrics.
66 """
67 self.collector.disableMetrics(names, objects)
68
69 def query(self, names, objects):
70 """ Retrieves collected metric values as well as some auxiliary
71 information. Returns an array of dictionaries, one dictionary per
72 metric. Each dictionary contains the following entries:
73 'name': metric name
74 'object': managed object this metric associated with
75 'unit': unit of measurement
76 'scale': divide 'values' by this number to get float numbers
77 'values': collected data
78 'values_as_string': pre-processed values ready for 'print' statement
79 """
80 (values, names_out, objects_out, units, scales, sequence_numbers,
81 indices, lengths) = self.collector.queryMetricsData(names, objects)
82 out = []
83 for i in xrange(0, len(names_out)):
84 scale = int(scales[i])
85 if scale != 1:
86 fmt = '%.2f%s'
87 else:
88 fmt = '%d %s'
89 out.append({
90 'name':str(names_out[i]),
91 'object':str(objects_out[i]),
92 'unit':str(units[i]),
93 'scale':scale,
94 'values':[int(values[j]) for j in xrange(int(indices[i]), int(indices[i])+int(lengths[i]))],
95 'values_as_string':'['+', '.join([fmt % (int(values[j])/scale, units[i]) for j in xrange(int(indices[i]), int(indices[i])+int(lengths[i]))])+']'
96 })
97 return out
98
99# Simple implementation of IConsoleCallback, one can use it as skeleton
100# for custom implementations
101class GuestMonitor:
102 def __init__(self, mach):
103 self.mach = mach
104
105 def onMousePointerShapeChange(self, visible, alpha, xHot, yHot, width, height, shape):
106 print "%s: onMousePointerShapeChange: visible=%d" %(self.mach.name, visible)
107 def onMouseCapabilityChange(self, supportsAbsolute, needsHostCursor):
108 print "%s: onMouseCapabilityChange: needsHostCursor=%d" %(self.mach.name, needsHostCursor)
109
110 def onKeyboardLedsChange(self, numLock, capsLock, scrollLock):
111 print "%s: onKeyboardLedsChange capsLock=%d" %(self.mach.name, capsLock)
112
113 def onStateChange(self, state):
114 print "%s: onStateChange state=%d" %(self.mach.name, state)
115
116 def onAdditionsStateChange(self):
117 print "%s: onAdditionsStateChange" %(self.mach.name)
118
119 def onDVDDriveChange(self):
120 print "%s: onDVDDriveChange" %(self.mach.name)
121
122 def onFloppyDriveChange(self):
123 print "%s: onFloppyDriveChange" %(self.mach.name)
124
125 def onNetworkAdapterChange(self, adapter):
126 print "%s: onNetworkAdapterChange" %(self.mach.name)
127
128 def onSerialPortChange(self, port):
129 print "%s: onSerialPortChange" %(self.mach.name)
130
131 def onParallelPortChange(self, port):
132 print "%s: onParallelPortChange" %(self.mach.name)
133
134 def onStorageControllerChange(self):
135 print "%s: onStorageControllerChange" %(self.mach.name)
136
137 def onVRDPServerChange(self):
138 print "%s: onVRDPServerChange" %(self.mach.name)
139
140 def onUSBControllerChange(self):
141 print "%s: onUSBControllerChange" %(self.mach.name)
142
143 def onUSBDeviceStateChange(self, device, attached, error):
144 print "%s: onUSBDeviceStateChange" %(self.mach.name)
145
146 def onSharedFolderChange(self, scope):
147 print "%s: onSharedFolderChange" %(self.mach.name)
148
149 def onRuntimeError(self, fatal, id, message):
150 print "%s: onRuntimeError fatal=%d message=%s" %(self.mach.name, fatal, message)
151
152 def onCanShowWindow(self):
153 print "%s: onCanShowWindow" %(self.mach.name)
154 return true
155
156 def onShowWindow(self, winId):
157 print "%s: onShowWindow: %d" %(self.mach.name, winId)
158
159g_hasreadline = 1
160try:
161 import readline
162 import rlcompleter
163except:
164 g_hasreadline = 0
165
166
167if g_hasreadline:
168 class CompleterNG(rlcompleter.Completer):
169 def __init__(self, dic, ctx):
170 self.ctx = ctx
171 return rlcompleter.Completer.__init__(self,dic)
172
173 def complete(self, text, state):
174 """
175 taken from:
176 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496812
177 """
178 if text == "":
179 return ['\t',None][state]
180 else:
181 return rlcompleter.Completer.complete(self,text,state)
182
183 def global_matches(self, text):
184 """
185 Compute matches when text is a simple name.
186 Return a list of all names currently defined
187 in self.namespace that match.
188 """
189
190 matches = []
191 n = len(text)
192
193 for list in [ self.namespace ]:
194 for word in list:
195 if word[:n] == text:
196 matches.append(word)
197
198
199 try:
200 for m in getMachines(self.ctx):
201 # although it has autoconversion, we need to cast
202 # explicitly for subscripts to work
203 word = str(m.name)
204 if word[:n] == text:
205 matches.append(word)
206 word = str(m.id)
207 if word[0] == '{':
208 word = word[1:-1]
209 if word[:n] == text:
210 matches.append(word)
211 except Exception,e:
212 traceback.print_exc()
213 print e
214
215 return matches
216
217
218def autoCompletion(commands, ctx):
219 if not g_hasreadline:
220 return
221
222 comps = {}
223 for (k,v) in commands.items():
224 comps[k] = None
225 completer = CompleterNG(comps, ctx)
226 readline.set_completer(completer.complete)
227 readline.parse_and_bind("tab: complete")
228
229g_verbose = True
230
231def split_no_quotes(s):
232 return s.split()
233
234def createVm(ctx,name,kind,base):
235 mgr = ctx['mgr']
236 vb = ctx['vb']
237 mach = vb.createMachine(name, kind, base,
238 "00000000-0000-0000-0000-000000000000")
239 mach.saveSettings()
240 print "created machine with UUID",mach.id
241 vb.registerMachine(mach)
242
243def removeVm(ctx,mach):
244 mgr = ctx['mgr']
245 vb = ctx['vb']
246 id = mach.id
247 print "removing machine ",mach.name,"with UUID",id
248 session = ctx['global'].openMachineSession(id)
249 mach=session.machine
250 for d in mach.getHardDiskAttachments():
251 mach.detachHardDisk(d.controller, d.port, d.device)
252 ctx['global'].closeMachineSession(session)
253 mach = vb.unregisterMachine(id)
254 if mach:
255 mach.deleteSettings()
256
257def startVm(ctx,mach,type):
258 mgr = ctx['mgr']
259 vb = ctx['vb']
260 perf = ctx['perf']
261 session = mgr.getSessionObject(vb)
262 uuid = mach.id
263 progress = vb.openRemoteSession(session, uuid, type, "")
264 progress.waitForCompletion(-1)
265 completed = progress.completed
266 rc = int(progress.resultCode)
267 print "Completed:", completed, "rc:",hex(rc&0xffffffff)
268 if rc == 0:
269 # we ignore exceptions to allow starting VM even if
270 # perf collector cannot be started
271 if perf:
272 try:
273 perf.setup(['*'], [mach], 10, 15)
274 except Exception,e:
275 print e
276 if g_verbose:
277 traceback.print_exc()
278 pass
279 # if session not opened, close doesn't make sense
280 session.close()
281 else:
282 # Not yet implemented error string query API for remote API
283 if not ctx['remote']:
284 print session.QueryErrorObject(rc)
285
286def getMachines(ctx):
287 return ctx['global'].getArray(ctx['vb'], 'machines')
288
289def asState(var):
290 if var:
291 return 'on'
292 else:
293 return 'off'
294
295def guestStats(ctx,mach):
296 if not ctx['perf']:
297 return
298 for metric in ctx['perf'].query(["*"], [mach]):
299 print metric['name'], metric['values_as_string']
300
301def guestExec(ctx, machine, console, cmds):
302 exec cmds
303
304def monitorGuest(ctx, machine, console, dur):
305 import time
306 cb = ctx['global'].createCallback('IConsoleCallback', GuestMonitor, machine)
307 console.registerCallback(cb)
308 if dur == -1:
309 # not infinity, but close enough
310 dur = 100000
311 end = time.clock() + dur
312 while time.clock() < end:
313 ctx['vb'].waitForEvents(100)
314 console.unregisterCallback(cb)
315
316def cmdExistingVm(ctx,mach,cmd,args):
317 mgr=ctx['mgr']
318 vb=ctx['vb']
319 session = mgr.getSessionObject(vb)
320 uuid = mach.id
321 try:
322 progress = vb.openExistingSession(session, uuid)
323 except Exception,e:
324 print "Session to '%s' not open: %s" %(mach.name,e)
325 if g_verbose:
326 traceback.print_exc()
327 return
328 if session.state != ctx['ifaces'].SessionState_Open:
329 print "Session to '%s' in wrong state: %s" %(mach.name, session.state)
330 return
331 # unfortunately IGuest is suppressed, thus WebServices knows not about it
332 # this is an example how to handle local only functionality
333 if ctx['remote'] and cmd == 'stats2':
334 print 'Trying to use local only functionality, ignored'
335 return
336 console=session.console
337 ops={'pause' : lambda: console.pause(),
338 'resume': lambda: console.resume(),
339 'powerdown': lambda: console.powerDown(),
340 'stats': lambda: guestStats(ctx, mach),
341 'guest': lambda: guestExec(ctx, mach, console, args),
342 'monitorGuest': lambda: monitorGuest(ctx, mach, console, args)
343 }
344 try:
345 ops[cmd]()
346 except Exception, e:
347 print 'failed: ',e
348 if g_verbose:
349 traceback.print_exc()
350
351 session.close()
352
353# can cache known machines, if needed
354def machById(ctx,id):
355 mach = None
356 for m in getMachines(ctx):
357 if m.name == id:
358 mach = m
359 break
360 mid = str(m.id)
361 if mid[0] == '{':
362 mid = mid[1:-1]
363 if mid == id:
364 mach = m
365 break
366 return mach
367
368def argsToMach(ctx,args):
369 if len(args) < 2:
370 print "usage: %s [vmname|uuid]" %(args[0])
371 return None
372 id = args[1]
373 m = machById(ctx, id)
374 if m == None:
375 print "Machine '%s' is unknown, use list command to find available machines" %(id)
376 return m
377
378def helpCmd(ctx, args):
379 if len(args) == 1:
380 print "Help page:"
381 for i in commands:
382 print " ",i,":", commands[i][0]
383 else:
384 c = commands.get(args[1], None)
385 if c == None:
386 print "Command '%s' not known" %(args[1])
387 else:
388 print " ",args[1],":", c[0]
389 return 0
390
391def listCmd(ctx, args):
392 for m in getMachines(ctx):
393 print "Machine '%s' [%s], state=%s" %(m.name,m.id,m.sessionState)
394 return 0
395
396def infoCmd(ctx,args):
397 if (len(args) < 2):
398 print "usage: info [vmname|uuid]"
399 return 0
400 mach = argsToMach(ctx,args)
401 if mach == None:
402 return 0
403 os = ctx['vb'].getGuestOSType(mach.OSTypeId)
404 print " Name: ",mach.name
405 print " ID: ",mach.id
406 print " OS Type: ",os.description
407 print " RAM: %dM" %(mach.memorySize)
408 print " VRAM: %dM" %(mach.VRAMSize)
409 print " Clipboard mode: %d" %(mach.clipboardMode)
410 print " Machine status: " ,mach.sessionState
411 bios = mach.BIOSSettings
412 print " BIOS ACPI: ",bios.ACPIEnabled
413 print " PAE: ",mach.PAEEnabled
414 print " Hardware virtualization: ",asState(mach.HWVirtExEnabled)
415 print " Nested paging: ",asState(mach.HWVirtExNestedPagingEnabled)
416 print " Last changed: ",mach.lastStateChange
417
418 return 0
419
420def startCmd(ctx, args):
421 mach = argsToMach(ctx,args)
422 if mach == None:
423 return 0
424 if len(args) > 2:
425 type = args[2]
426 else:
427 type = "gui"
428 startVm(ctx, mach, type)
429 return 0
430
431def createCmd(ctx, args):
432 if (len(args) < 3 or len(args) > 4):
433 print "usage: create name ostype <basefolder>"
434 return 0
435 name = args[1]
436 oskind = args[2]
437 if len(args) == 4:
438 base = args[3]
439 else:
440 base = ''
441 try:
442 ctx['vb'].getGuestOSType(oskind)
443 except Exception, e:
444 print 'Unknown OS type:',oskind
445 return 0
446 createVm(ctx, name, oskind, base)
447 return 0
448
449def removeCmd(ctx, args):
450 mach = argsToMach(ctx,args)
451 if mach == None:
452 return 0
453 removeVm(ctx, mach)
454 return 0
455
456def pauseCmd(ctx, args):
457 mach = argsToMach(ctx,args)
458 if mach == None:
459 return 0
460 cmdExistingVm(ctx, mach, 'pause', '')
461 return 0
462
463def powerdownCmd(ctx, args):
464 mach = argsToMach(ctx,args)
465 if mach == None:
466 return 0
467 cmdExistingVm(ctx, mach, 'powerdown', '')
468 return 0
469
470def resumeCmd(ctx, args):
471 mach = argsToMach(ctx,args)
472 if mach == None:
473 return 0
474 cmdExistingVm(ctx, mach, 'resume', '')
475 return 0
476
477def statsCmd(ctx, args):
478 mach = argsToMach(ctx,args)
479 if mach == None:
480 return 0
481 cmdExistingVm(ctx, mach, 'stats', '')
482 return 0
483
484def guestCmd(ctx, args):
485 if (len(args) < 3):
486 print "usage: guest name commands"
487 return 0
488 mach = argsToMach(ctx,args)
489 if mach == None:
490 return 0
491 cmdExistingVm(ctx, mach, 'guest', ' '.join(args[2:]))
492 return 0
493
494def setvarCmd(ctx, args):
495 if (len(args) < 4):
496 print "usage: setvar [vmname|uuid] expr value"
497 return 0
498 mach = argsToMach(ctx,args)
499 if mach == None:
500 return 0
501 session = ctx['mgr'].getSessionObject(vbox)
502 vbox.openSession(session, mach.id)
503 mach = session.machine
504 expr = 'mach.'+args[2]+' = '+args[3]
505 print "Executing",expr
506 try:
507 exec expr
508 except Exception, e:
509 print 'failed: ',e
510 if g_verbose:
511 traceback.print_exc()
512 mach.saveSettings()
513 session.close()
514 return 0
515
516def quitCmd(ctx, args):
517 return 1
518
519def aliasesCmd(ctx, args):
520 for (k,v) in aliases.items():
521 print "'%s' is an alias for '%s'" %(k,v)
522 return 0
523
524def verboseCmd(ctx, args):
525 global g_verbose
526 g_verbose = not g_verbose
527 return 0
528
529def hostCmd(ctx, args):
530 host = ctx['vb'].host
531 cnt = host.processorCount
532 print "Processor count:",cnt
533 for i in range(0,cnt):
534 print "Processor #%d speed: %dMHz" %(i,host.getProcessorSpeed(i))
535
536 if ctx['perf']:
537 for metric in ctx['perf'].query(["*"], [host]):
538 print metric['name'], metric['values_as_string']
539
540 return 0
541
542
543def monitorGuestCmd(ctx, args):
544 if (len(args) < 2):
545 print "usage: monitorGuest name (duration)"
546 return 0
547 mach = argsToMach(ctx,args)
548 if mach == None:
549 return 0
550 dur = 5
551 if len(args) > 2:
552 dur = float(args[2])
553 cmdExistingVm(ctx, mach, 'monitorGuest', dur)
554 return 0
555
556def evalCmd(ctx, args):
557 expr = ' '.join(args[1:])
558 try:
559 exec expr
560 except Exception, e:
561 print 'failed: ',e
562 if g_verbose:
563 traceback.print_exc()
564 return 0
565
566aliases = {'s':'start',
567 'i':'info',
568 'l':'list',
569 'h':'help',
570 'a':'aliases',
571 'q':'quit', 'exit':'quit',
572 'v':'verbose'}
573
574commands = {'help':['Prints help information', helpCmd],
575 'start':['Start virtual machine by name or uuid', startCmd],
576 'create':['Create virtual machine', createCmd],
577 'remove':['Remove virtual machine', removeCmd],
578 'pause':['Pause virtual machine', pauseCmd],
579 'resume':['Resume virtual machine', resumeCmd],
580 'stats':['Stats for virtual machine', statsCmd],
581 'powerdown':['Power down virtual machine', powerdownCmd],
582 'list':['Shows known virtual machines', listCmd],
583 'info':['Shows info on machine', infoCmd],
584 'aliases':['Shows aliases', aliasesCmd],
585 'verbose':['Toggle verbosity', verboseCmd],
586 'setvar':['Set VMs variable: setvar Fedora BIOSSettings.ACPIEnabled True', setvarCmd],
587 'eval':['Evaluate arbitrary Python construction: eval for m in getMachines(ctx): print m.name,"has",m.memorySize,"M"', evalCmd],
588 'quit':['Exits', quitCmd],
589 'host':['Show host information', hostCmd],
590 'guest':['Execute command for guest: guest Win32 console.mouse.putMouseEvent(20, 20, 0, 0)', guestCmd],
591 'monitorGuest':['Monitor what happens with the guest for some time: monitorGuest Win32 10', monitorGuestCmd],
592 }
593
594def runCommand(ctx, cmd):
595 if len(cmd) == 0: return 0
596 args = split_no_quotes(cmd)
597 if len(args) == 0: return 0
598 c = args[0]
599 if aliases.get(c, None) != None:
600 c = aliases[c]
601 ci = commands.get(c,None)
602 if ci == None:
603 print "Unknown command: '%s', type 'help' for list of known commands" %(c)
604 return 0
605 return ci[1](ctx, args)
606
607
608def interpret(ctx):
609 vbox = ctx['vb']
610 print "Running VirtualBox version %s" %(vbox.version)
611 ctx['perf'] = PerfCollector(vbox)
612
613 autoCompletion(commands, ctx)
614
615 # to allow to print actual host information, we collect info for
616 # last 150 secs maximum, (sample every 10 secs and keep up to 15 samples)
617 if ctx['perf']:
618 try:
619 ctx['perf'].setup(['*'], [vbox.host], 10, 15)
620 except:
621 pass
622
623 while True:
624 try:
625 cmd = raw_input("vbox> ")
626 vbox.waitForEvents(0)
627 done = runCommand(ctx, cmd)
628 if done != 0: break
629 except KeyboardInterrupt:
630 print '====== You can type quit or q to leave'
631 break
632 except EOFError:
633 break;
634 except Exception,e:
635 print e
636 if g_verbose:
637 traceback.print_exc()
638
639 try:
640 # There is no need to disable metric collection. This is just an example.
641 if ct['perf']:
642 ctx['perf'].disable(['*'], [vbox.host])
643 except:
644 pass
645
646
647from vboxapi import VirtualBoxManager
648
649def main(argv):
650 style = None
651 if len(argv) > 1:
652 if argv[1] == "-w":
653 style = "WEBSERVICE"
654
655 g_virtualBoxManager = VirtualBoxManager(style, None)
656 ctx = {'global':g_virtualBoxManager,
657 'mgr':g_virtualBoxManager.mgr,
658 'vb':g_virtualBoxManager.vbox,
659 'ifaces':g_virtualBoxManager.constants,
660 'remote':g_virtualBoxManager.remote,
661 'type':g_virtualBoxManager.type
662 }
663 interpret(ctx)
664 del g_virtualBoxManager
665
666if __name__ == '__main__':
667 main(sys.argv)
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette