VirtualBox

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

Last change on this file since 86223 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 119.4 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: vboxshell.py 82968 2020-02-04 10:35:17Z vboxsync $
4
5"""
6VirtualBox Python Shell.
7
8This program is a simple interactive shell for VirtualBox. You can query
9information and issue commands from a simple command line.
10
11It also provides you with examples on how to use VirtualBox's Python API.
12This shell is even somewhat documented, supports TAB-completion and
13history if you have Python readline installed.
14
15Finally, shell allows arbitrary custom extensions, just create
16.VirtualBox/shexts/ and drop your extensions there.
17 Enjoy.
18
19P.S. Our apologies for the code quality.
20"""
21
22from __future__ import print_function
23
24__copyright__ = \
25"""
26Copyright (C) 2009-2020 Oracle Corporation
27
28This file is part of VirtualBox Open Source Edition (OSE), as
29available from http://www.virtualbox.org. This file is free software;
30you can redistribute it and/or modify it under the terms of the GNU
31General Public License (GPL) as published by the Free Software
32Foundation, in version 2 as it comes in the "COPYING" file of the
33VirtualBox OSE distribution. VirtualBox OSE is distributed in the
34hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
35"""
36__version__ = "$Revision: 82968 $"
37
38
39import gc
40import os
41import sys
42import traceback
43import shlex
44import time
45import re
46import platform
47from optparse import OptionParser
48from pprint import pprint
49
50
51#
52# Global Variables
53#
54g_fBatchMode = False
55g_sScriptFile = None
56g_sCmd = None
57g_fHasReadline = True
58try:
59 import readline
60 import rlcompleter
61except ImportError:
62 g_fHasReadline = False
63
64g_sPrompt = "vbox> "
65
66g_fHasColors = True
67g_dTermColors = {
68 'red': '\033[31m',
69 'blue': '\033[94m',
70 'green': '\033[92m',
71 'yellow': '\033[93m',
72 'magenta': '\033[35m',
73 'cyan': '\033[36m'
74}
75
76
77
78def colored(strg, color):
79 """
80 Translates a string to one including coloring settings, if enabled.
81 """
82 if not g_fHasColors:
83 return strg
84 col = g_dTermColors.get(color, None)
85 if col:
86 return col+str(strg)+'\033[0m'
87 return strg
88
89if g_fHasReadline:
90 class CompleterNG(rlcompleter.Completer):
91 def __init__(self, dic, ctx):
92 self.ctx = ctx
93 rlcompleter.Completer.__init__(self, dic)
94
95 def complete(self, text, state):
96 """
97 taken from:
98 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496812
99 """
100 if False and text == "":
101 return ['\t', None][state]
102 else:
103 return rlcompleter.Completer.complete(self, text, state)
104
105 def canBePath(self, _phrase, word):
106 return word.startswith('/')
107
108 def canBeCommand(self, phrase, _word):
109 spaceIdx = phrase.find(" ")
110 begIdx = readline.get_begidx()
111 firstWord = (spaceIdx == -1 or begIdx < spaceIdx)
112 if firstWord:
113 return True
114 if phrase.startswith('help'):
115 return True
116 return False
117
118 def canBeMachine(self, phrase, word):
119 return not self.canBePath(phrase, word) and not self.canBeCommand(phrase, word)
120
121 def global_matches(self, text):
122 """
123 Compute matches when text is a simple name.
124 Return a list of all names currently defined
125 in self.namespace that match.
126 """
127
128 matches = []
129 phrase = readline.get_line_buffer()
130
131 try:
132 if self.canBePath(phrase, text):
133 (directory, rest) = os.path.split(text)
134 c = len(rest)
135 for word in os.listdir(directory):
136 if c == 0 or word[:c] == rest:
137 matches.append(os.path.join(directory, word))
138
139 if self.canBeCommand(phrase, text):
140 c = len(text)
141 for lst in [ self.namespace ]:
142 for word in lst:
143 if word[:c] == text:
144 matches.append(word)
145
146 if self.canBeMachine(phrase, text):
147 c = len(text)
148 for mach in getMachines(self.ctx, False, True):
149 # although it has autoconversion, we need to cast
150 # explicitly for subscripts to work
151 word = re.sub("(?<!\\\\) ", "\\ ", str(mach.name))
152 if word[:c] == text:
153 matches.append(word)
154 word = str(mach.id)
155 if word[:c] == text:
156 matches.append(word)
157
158 except Exception as e:
159 printErr(self.ctx, e)
160 if g_fVerbose:
161 traceback.print_exc()
162
163 return matches
164
165def autoCompletion(cmds, ctx):
166 if not g_fHasReadline:
167 return
168
169 comps = {}
170 for (key, _value) in list(cmds.items()):
171 comps[key] = None
172 completer = CompleterNG(comps, ctx)
173 readline.set_completer(completer.complete)
174 delims = readline.get_completer_delims()
175 readline.set_completer_delims(re.sub("[\\./-]", "", delims)) # remove some of the delimiters
176 readline.parse_and_bind("set editing-mode emacs")
177 # OSX need it
178 if platform.system() == 'Darwin':
179 # see http://www.certif.com/spec_help/readline.html
180 readline.parse_and_bind ("bind ^I rl_complete")
181 readline.parse_and_bind ("bind ^W ed-delete-prev-word")
182 # Doesn't work well
183 # readline.parse_and_bind ("bind ^R em-inc-search-prev")
184 readline.parse_and_bind("tab: complete")
185
186
187g_fVerbose = False
188
189def split_no_quotes(s):
190 return shlex.split(s)
191
192def progressBar(ctx, progress, wait=1000):
193 try:
194 while not progress.completed:
195 print("%s %%\r" % (colored(str(progress.percent), 'red')), end="")
196 sys.stdout.flush()
197 progress.waitForCompletion(wait)
198 ctx['global'].waitForEvents(0)
199 if int(progress.resultCode) != 0:
200 reportError(ctx, progress)
201 return 1
202 except KeyboardInterrupt:
203 print("Interrupted.")
204 ctx['interrupt'] = True
205 if progress.cancelable:
206 print("Canceling task...")
207 progress.cancel()
208 return 0
209
210def printErr(_ctx, e):
211 oVBoxMgr = _ctx['global']
212 if oVBoxMgr.errIsOurXcptKind(e):
213 print(colored('%s: %s' % (oVBoxMgr.xcptToString(e), oVBoxMgr.xcptGetMessage(e)), 'red'))
214 else:
215 print(colored(str(e), 'red'))
216
217def reportError(_ctx, progress):
218 errorinfo = progress.errorInfo
219 if errorinfo:
220 print(colored("Error in module '%s': %s" % (errorinfo.component, errorinfo.text), 'red'))
221
222def colCat(_ctx, strg):
223 return colored(strg, 'magenta')
224
225def colVm(_ctx, vmname):
226 return colored(vmname, 'blue')
227
228def colPath(_ctx, path):
229 return colored(path, 'green')
230
231def colSize(_ctx, byte):
232 return colored(byte, 'red')
233
234def colPci(_ctx, pcidev):
235 return colored(pcidev, 'green')
236
237def colDev(_ctx, pcidev):
238 return colored(pcidev, 'cyan')
239
240def colSizeM(_ctx, mbyte):
241 return colored(str(mbyte)+'M', 'red')
242
243def createVm(ctx, name, kind):
244 vbox = ctx['vb']
245 mach = vbox.createMachine("", name, [], kind, "")
246 mach.saveSettings()
247 print("created machine with UUID", mach.id)
248 vbox.registerMachine(mach)
249 # update cache
250 getMachines(ctx, True)
251
252def removeVm(ctx, mach):
253 uuid = mach.id
254 print("removing machine ", mach.name, "with UUID", uuid)
255 cmdClosedVm(ctx, mach, detachVmDevice, ["ALL"])
256 disks = mach.unregister(ctx['global'].constants.CleanupMode_Full)
257 if mach:
258 progress = mach.deleteConfig(disks)
259 if progressBar(ctx, progress, 100) and int(progress.resultCode) == 0:
260 print("Success!")
261 else:
262 reportError(ctx, progress)
263 # update cache
264 getMachines(ctx, True)
265
266def startVm(ctx, mach, vmtype):
267 vbox = ctx['vb']
268 perf = ctx['perf']
269 session = ctx['global'].getSessionObject()
270 asEnv = []
271 progress = mach.launchVMProcess(session, vmtype, asEnv)
272 if progressBar(ctx, progress, 100) and int(progress.resultCode) == 0:
273 # we ignore exceptions to allow starting VM even if
274 # perf collector cannot be started
275 if perf:
276 try:
277 perf.setup(['*'], [mach], 10, 15)
278 except Exception as e:
279 printErr(ctx, e)
280 if g_fVerbose:
281 traceback.print_exc()
282 session.unlockMachine()
283
284class CachedMach:
285 def __init__(self, mach):
286 if mach.accessible:
287 self.name = mach.name
288 else:
289 self.name = '<inaccessible>'
290 self.id = mach.id
291
292def cacheMachines(_ctx, lst):
293 result = []
294 for mach in lst:
295 elem = CachedMach(mach)
296 result.append(elem)
297 return result
298
299def getMachines(ctx, invalidate = False, simple=False):
300 if ctx['vb'] is not None:
301 if ctx['_machlist'] is None or invalidate:
302 ctx['_machlist'] = ctx['global'].getArray(ctx['vb'], 'machines')
303 ctx['_machlistsimple'] = cacheMachines(ctx, ctx['_machlist'])
304 if simple:
305 return ctx['_machlistsimple']
306 else:
307 return ctx['_machlist']
308 else:
309 return []
310
311def asState(var):
312 if var:
313 return colored('on', 'green')
314 else:
315 return colored('off', 'green')
316
317def asFlag(var):
318 if var:
319 return 'yes'
320 else:
321 return 'no'
322
323def getFacilityStatus(ctx, guest, facilityType):
324 (status, _timestamp) = guest.getFacilityStatus(facilityType)
325 return asEnumElem(ctx, 'AdditionsFacilityStatus', status)
326
327def perfStats(ctx, mach):
328 if not ctx['perf']:
329 return
330 for metric in ctx['perf'].query(["*"], [mach]):
331 print(metric['name'], metric['values_as_string'])
332
333def guestExec(ctx, machine, console, cmds):
334 exec(cmds)
335
336def printMouseEvent(_ctx, mev):
337 print("Mouse : mode=%d x=%d y=%d z=%d w=%d buttons=%x" % (mev.mode, mev.x, mev.y, mev.z, mev.w, mev.buttons))
338
339def printKbdEvent(ctx, kev):
340 print("Kbd: ", ctx['global'].getArray(kev, 'scancodes'))
341
342def printMultiTouchEvent(ctx, mtev):
343 print("MultiTouch : contacts=%d time=%d" % (mtev.contactCount, mtev.scanTime))
344 xPositions = ctx['global'].getArray(mtev, 'xPositions')
345 yPositions = ctx['global'].getArray(mtev, 'yPositions')
346 contactIds = ctx['global'].getArray(mtev, 'contactIds')
347 contactFlags = ctx['global'].getArray(mtev, 'contactFlags')
348
349 for i in range(0, mtev.contactCount):
350 print(" [%d] %d,%d %d %d" % (i, xPositions[i], yPositions[i], contactIds[i], contactFlags[i]))
351
352def monitorSource(ctx, eventSource, active, dur):
353 def handleEventImpl(event):
354 evtype = event.type
355 print("got event: %s %s" % (str(evtype), asEnumElem(ctx, 'VBoxEventType', evtype)))
356 if evtype == ctx['global'].constants.VBoxEventType_OnMachineStateChanged:
357 scev = ctx['global'].queryInterface(event, 'IMachineStateChangedEvent')
358 if scev:
359 print("machine state event: mach=%s state=%s" % (scev.machineId, scev.state))
360 elif evtype == ctx['global'].constants.VBoxEventType_OnSnapshotTaken:
361 stev = ctx['global'].queryInterface(event, 'ISnapshotTakenEvent')
362 if stev:
363 print("snapshot taken event: mach=%s snap=%s" % (stev.machineId, stev.snapshotId))
364 elif evtype == ctx['global'].constants.VBoxEventType_OnGuestPropertyChanged:
365 gpcev = ctx['global'].queryInterface(event, 'IGuestPropertyChangedEvent')
366 if gpcev:
367 print("guest property change: name=%s value=%s" % (gpcev.name, gpcev.value))
368 elif evtype == ctx['global'].constants.VBoxEventType_OnMousePointerShapeChanged:
369 psev = ctx['global'].queryInterface(event, 'IMousePointerShapeChangedEvent')
370 if psev:
371 shape = ctx['global'].getArray(psev, 'shape')
372 if shape is None:
373 print("pointer shape event - empty shape")
374 else:
375 print("pointer shape event: w=%d h=%d shape len=%d" % (psev.width, psev.height, len(shape)))
376 elif evtype == ctx['global'].constants.VBoxEventType_OnGuestMouse:
377 mev = ctx['global'].queryInterface(event, 'IGuestMouseEvent')
378 if mev:
379 printMouseEvent(ctx, mev)
380 elif evtype == ctx['global'].constants.VBoxEventType_OnGuestKeyboard:
381 kev = ctx['global'].queryInterface(event, 'IGuestKeyboardEvent')
382 if kev:
383 printKbdEvent(ctx, kev)
384 elif evtype == ctx['global'].constants.VBoxEventType_OnGuestMultiTouch:
385 mtev = ctx['global'].queryInterface(event, 'IGuestMultiTouchEvent')
386 if mtev:
387 printMultiTouchEvent(ctx, mtev)
388
389 class EventListener(object):
390 def __init__(self, arg):
391 pass
392
393 def handleEvent(self, event):
394 try:
395 # a bit convoluted QI to make it work with MS COM
396 handleEventImpl(ctx['global'].queryInterface(event, 'IEvent'))
397 except:
398 traceback.print_exc()
399 pass
400
401 if active:
402 listener = ctx['global'].createListener(EventListener)
403 else:
404 listener = eventSource.createListener()
405 registered = False
406 if dur == -1:
407 # not infinity, but close enough
408 dur = 100000
409 try:
410 eventSource.registerListener(listener, [ctx['global'].constants.VBoxEventType_Any], active)
411 registered = True
412 end = time.time() + dur
413 while time.time() < end:
414 if active:
415 ctx['global'].waitForEvents(500)
416 else:
417 event = eventSource.getEvent(listener, 500)
418 if event:
419 handleEventImpl(event)
420 # otherwise waitable events will leak (active listeners ACK automatically)
421 eventSource.eventProcessed(listener, event)
422 # We need to catch all exceptions here, otherwise listener will never be unregistered
423 except:
424 traceback.print_exc()
425 pass
426 if listener and registered:
427 eventSource.unregisterListener(listener)
428
429
430g_tsLast = 0
431def recordDemo(ctx, console, filename, dur):
432 demo = open(filename, 'w')
433 header = "VM=" + console.machine.name + "\n"
434 demo.write(header)
435
436 global g_tsLast
437 g_tsLast = time.time()
438
439 def stamp():
440 global g_tsLast
441 tsCur = time.time()
442 timePassed = int((tsCur-g_tsLast)*1000)
443 g_tsLast = tsCur
444 return timePassed
445
446 def handleEventImpl(event):
447 evtype = event.type
448 #print("got event: %s %s" % (str(evtype), asEnumElem(ctx, 'VBoxEventType', evtype)))
449 if evtype == ctx['global'].constants.VBoxEventType_OnGuestMouse:
450 mev = ctx['global'].queryInterface(event, 'IGuestMouseEvent')
451 if mev:
452 line = "%d: m %d %d %d %d %d %d\n" % (stamp(), mev.mode, mev.x, mev.y, mev.z, mev.w, mev.buttons)
453 demo.write(line)
454 elif evtype == ctx['global'].constants.VBoxEventType_OnGuestKeyboard:
455 kev = ctx['global'].queryInterface(event, 'IGuestKeyboardEvent')
456 if kev:
457 line = "%d: k %s\n" % (stamp(), str(ctx['global'].getArray(kev, 'scancodes')))
458 demo.write(line)
459
460 listener = console.eventSource.createListener()
461 registered = False
462 # we create an aggregated event source to listen for multiple event sources (keyboard and mouse in our case)
463 agg = console.eventSource.createAggregator([console.keyboard.eventSource, console.mouse.eventSource])
464 demo = open(filename, 'w')
465 header = "VM=" + console.machine.name + "\n"
466 demo.write(header)
467 if dur == -1:
468 # not infinity, but close enough
469 dur = 100000
470 try:
471 agg.registerListener(listener, [ctx['global'].constants.VBoxEventType_Any], False)
472 registered = True
473 end = time.time() + dur
474 while time.time() < end:
475 event = agg.getEvent(listener, 1000)
476 if event:
477 handleEventImpl(event)
478 # keyboard/mouse events aren't waitable, so no need for eventProcessed
479 # We need to catch all exceptions here, otherwise listener will never be unregistered
480 except:
481 traceback.print_exc()
482 pass
483 demo.close()
484 if listener and registered:
485 agg.unregisterListener(listener)
486
487
488def playbackDemo(ctx, console, filename, dur):
489 demo = open(filename, 'r')
490
491 if dur == -1:
492 # not infinity, but close enough
493 dur = 100000
494
495 header = demo.readline()
496 print("Header is", header)
497 basere = re.compile(r'(?P<s>\d+): (?P<t>[km]) (?P<p>.*)')
498 mre = re.compile(r'(?P<a>\d+) (?P<x>-*\d+) (?P<y>-*\d+) (?P<z>-*\d+) (?P<w>-*\d+) (?P<b>-*\d+)')
499 kre = re.compile(r'\d+')
500
501 kbd = console.keyboard
502 mouse = console.mouse
503
504 try:
505 end = time.time() + dur
506 for line in demo:
507 if time.time() > end:
508 break
509 match = basere.search(line)
510 if match is None:
511 continue
512
513 rdict = match.groupdict()
514 stamp = rdict['s']
515 params = rdict['p']
516 rtype = rdict['t']
517
518 time.sleep(float(stamp)/1000)
519
520 if rtype == 'k':
521 codes = kre.findall(params)
522 #print("KBD:", codes)
523 kbd.putScancodes(codes)
524 elif rtype == 'm':
525 mm = mre.search(params)
526 if mm is not None:
527 mdict = mm.groupdict()
528 if mdict['a'] == '1':
529 # absolute
530 #print("MA: ", mdict['x'], mdict['y'], mdict['z'], mdict['b'])
531 mouse.putMouseEventAbsolute(int(mdict['x']), int(mdict['y']), int(mdict['z']), int(mdict['w']), int(mdict['b']))
532 else:
533 #print("MR: ", mdict['x'], mdict['y'], mdict['b'])
534 mouse.putMouseEvent(int(mdict['x']), int(mdict['y']), int(mdict['z']), int(mdict['w']), int(mdict['b']))
535
536 # We need to catch all exceptions here, to close file
537 except KeyboardInterrupt:
538 ctx['interrupt'] = True
539 except:
540 traceback.print_exc()
541 pass
542 demo.close()
543
544
545def takeScreenshotOld(_ctx, console, args):
546 from PIL import Image
547 display = console.display
548 if len(args) > 0:
549 f = args[0]
550 else:
551 f = "/tmp/screenshot.png"
552 if len(args) > 3:
553 screen = int(args[3])
554 else:
555 screen = 0
556 (fbw, fbh, _fbbpp, fbx, fby, _) = display.getScreenResolution(screen)
557 if len(args) > 1:
558 w = int(args[1])
559 else:
560 w = fbw
561 if len(args) > 2:
562 h = int(args[2])
563 else:
564 h = fbh
565
566 print("Saving screenshot (%d x %d) screen %d in %s..." % (w, h, screen, f))
567 data = display.takeScreenShotToArray(screen, w, h, ctx['const'].BitmapFormat_RGBA)
568 size = (w, h)
569 mode = "RGBA"
570 im = Image.frombuffer(mode, size, str(data), "raw", mode, 0, 1)
571 im.save(f, "PNG")
572
573def takeScreenshot(_ctx, console, args):
574 display = console.display
575 if len(args) > 0:
576 f = args[0]
577 else:
578 f = "/tmp/screenshot.png"
579 if len(args) > 3:
580 screen = int(args[3])
581 else:
582 screen = 0
583 (fbw, fbh, _fbbpp, fbx, fby, _) = display.getScreenResolution(screen)
584 if len(args) > 1:
585 w = int(args[1])
586 else:
587 w = fbw
588 if len(args) > 2:
589 h = int(args[2])
590 else:
591 h = fbh
592
593 print("Saving screenshot (%d x %d) screen %d in %s..." % (w, h, screen, f))
594 data = display.takeScreenShotToArray(screen, w, h, ctx['const'].BitmapFormat_PNG)
595 pngfile = open(f, 'wb')
596 pngfile.write(data)
597 pngfile.close()
598
599def teleport(ctx, _session, console, args):
600 if args[0].find(":") == -1:
601 print("Use host:port format for teleport target")
602 return
603 (host, port) = args[0].split(":")
604 if len(args) > 1:
605 passwd = args[1]
606 else:
607 passwd = ""
608
609 if len(args) > 2:
610 maxDowntime = int(args[2])
611 else:
612 maxDowntime = 250
613
614 port = int(port)
615 print("Teleporting to %s:%d..." % (host, port))
616 progress = console.teleport(host, port, passwd, maxDowntime)
617 if progressBar(ctx, progress, 100) and int(progress.resultCode) == 0:
618 print("Success!")
619 else:
620 reportError(ctx, progress)
621
622
623def guestStats(ctx, console, args):
624 guest = console.guest
625 # we need to set up guest statistics
626 if len(args) > 0 :
627 update = args[0]
628 else:
629 update = 1
630 if guest.statisticsUpdateInterval != update:
631 guest.statisticsUpdateInterval = update
632 try:
633 time.sleep(float(update)+0.1)
634 except:
635 # to allow sleep interruption
636 pass
637 all_stats = ctx['const'].all_values('GuestStatisticType')
638 cpu = 0
639 for s in list(all_stats.keys()):
640 try:
641 val = guest.getStatistic( cpu, all_stats[s])
642 print("%s: %d" % (s, val))
643 except:
644 # likely not implemented
645 pass
646
647def plugCpu(_ctx, machine, _session, args):
648 cpu = int(args[0])
649 print("Adding CPU %d..." % (cpu))
650 machine.hotPlugCPU(cpu)
651
652def unplugCpu(_ctx, machine, _session, args):
653 cpu = int(args[0])
654 print("Removing CPU %d..." % (cpu))
655 machine.hotUnplugCPU(cpu)
656
657def mountIso(_ctx, machine, _session, args):
658 machine.mountMedium(args[0], args[1], args[2], args[3], args[4])
659 machine.saveSettings()
660
661def cond(c, v1, v2):
662 if c:
663 return v1
664 else:
665 return v2
666
667def printHostUsbDev(ctx, ud):
668 print(" %s: %s (vendorId=%d productId=%d serial=%s) %s" % (ud.id, colored(ud.product, 'blue'), ud.vendorId, ud.productId, ud.serialNumber, asEnumElem(ctx, 'USBDeviceState', ud.state)))
669
670def printUsbDev(_ctx, ud):
671 print(" %s: %s (vendorId=%d productId=%d serial=%s)" % (ud.id, colored(ud.product, 'blue'), ud.vendorId, ud.productId, ud.serialNumber))
672
673def printSf(ctx, sf):
674 print(" name=%s host=%s %s %s" % (sf.name, colPath(ctx, sf.hostPath), cond(sf.accessible, "accessible", "not accessible"), cond(sf.writable, "writable", "read-only")))
675
676def ginfo(ctx, console, _args):
677 guest = console.guest
678 if guest.additionsRunLevel != ctx['const'].AdditionsRunLevelType_None:
679 print("Additions active, version %s" % (guest.additionsVersion))
680 print("Support seamless: %s" % (getFacilityStatus(ctx, guest, ctx['const'].AdditionsFacilityType_Seamless)))
681 print("Support graphics: %s" % (getFacilityStatus(ctx, guest, ctx['const'].AdditionsFacilityType_Graphics)))
682 print("Balloon size: %d" % (guest.memoryBalloonSize))
683 print("Statistic update interval: %d" % (guest.statisticsUpdateInterval))
684 else:
685 print("No additions")
686 usbs = ctx['global'].getArray(console, 'USBDevices')
687 print("Attached USB:")
688 for ud in usbs:
689 printUsbDev(ctx, ud)
690 rusbs = ctx['global'].getArray(console, 'remoteUSBDevices')
691 print("Remote USB:")
692 for ud in rusbs:
693 printHostUsbDev(ctx, ud)
694 print("Transient shared folders:")
695 sfs = rusbs = ctx['global'].getArray(console, 'sharedFolders')
696 for sf in sfs:
697 printSf(ctx, sf)
698
699def cmdExistingVm(ctx, mach, cmd, args):
700 session = None
701 try:
702 vbox = ctx['vb']
703 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
704 except Exception as e:
705 printErr(ctx, "Session to '%s' not open: %s" % (mach.name, str(e)))
706 if g_fVerbose:
707 traceback.print_exc()
708 return
709 if session.state != ctx['const'].SessionState_Locked:
710 print("Session to '%s' in wrong state: %s" % (mach.name, session.state))
711 session.unlockMachine()
712 return
713 # this could be an example how to handle local only (i.e. unavailable
714 # in Webservices) functionality
715 if ctx['remote'] and cmd == 'some_local_only_command':
716 print('Trying to use local only functionality, ignored')
717 session.unlockMachine()
718 return
719 console = session.console
720 ops = {'pause': lambda: console.pause(),
721 'resume': lambda: console.resume(),
722 'powerdown': lambda: console.powerDown(),
723 'powerbutton': lambda: console.powerButton(),
724 'stats': lambda: perfStats(ctx, mach),
725 'guest': lambda: guestExec(ctx, mach, console, args),
726 'ginfo': lambda: ginfo(ctx, console, args),
727 'guestlambda': lambda: args[0](ctx, mach, console, args[1:]),
728 'save': lambda: progressBar(ctx, session.machine.saveState()),
729 'screenshot': lambda: takeScreenshot(ctx, console, args),
730 'teleport': lambda: teleport(ctx, session, console, args),
731 'gueststats': lambda: guestStats(ctx, console, args),
732 'plugcpu': lambda: plugCpu(ctx, session.machine, session, args),
733 'unplugcpu': lambda: unplugCpu(ctx, session.machine, session, args),
734 'mountiso': lambda: mountIso(ctx, session.machine, session, args),
735 }
736 try:
737 ops[cmd]()
738 except KeyboardInterrupt:
739 ctx['interrupt'] = True
740 except Exception as e:
741 printErr(ctx, e)
742 if g_fVerbose:
743 traceback.print_exc()
744
745 session.unlockMachine()
746
747
748def cmdClosedVm(ctx, mach, cmd, args=[], save=True):
749 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
750 mach = session.machine
751 try:
752 cmd(ctx, mach, args)
753 except Exception as e:
754 save = False
755 printErr(ctx, e)
756 if g_fVerbose:
757 traceback.print_exc()
758 if save:
759 try:
760 mach.saveSettings()
761 except Exception as e:
762 printErr(ctx, e)
763 if g_fVerbose:
764 traceback.print_exc()
765 ctx['global'].closeMachineSession(session)
766
767
768def cmdAnyVm(ctx, mach, cmd, args=[], save=False):
769 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
770 mach = session.machine
771 try:
772 cmd(ctx, mach, session.console, args)
773 except Exception as e:
774 save = False
775 printErr(ctx, e)
776 if g_fVerbose:
777 traceback.print_exc()
778 if save:
779 mach.saveSettings()
780 ctx['global'].closeMachineSession(session)
781
782def machById(ctx, uuid):
783 mach = ctx['vb'].findMachine(uuid)
784 return mach
785
786class XPathNode:
787 def __init__(self, parent, obj, ntype):
788 self.parent = parent
789 self.obj = obj
790 self.ntype = ntype
791 def lookup(self, subpath):
792 children = self.enum()
793 matches = []
794 for e in children:
795 if e.matches(subpath):
796 matches.append(e)
797 return matches
798 def enum(self):
799 return []
800 def matches(self, subexp):
801 if subexp == self.ntype:
802 return True
803 if not subexp.startswith(self.ntype):
804 return False
805 match = re.search(r"@(?P<a>\w+)=(?P<v>[^\'\[\]]+)", subexp)
806 matches = False
807 try:
808 if match is not None:
809 xdict = match.groupdict()
810 attr = xdict['a']
811 val = xdict['v']
812 matches = (str(getattr(self.obj, attr)) == val)
813 except:
814 pass
815 return matches
816 def apply(self, cmd):
817 exec(cmd, {'obj':self.obj, 'node':self, 'ctx':self.getCtx()}, {})
818 def getCtx(self):
819 if hasattr(self, 'ctx'):
820 return self.ctx
821 return self.parent.getCtx()
822
823class XPathNodeHolder(XPathNode):
824 def __init__(self, parent, obj, attr, heldClass, xpathname):
825 XPathNode.__init__(self, parent, obj, 'hld '+xpathname)
826 self.attr = attr
827 self.heldClass = heldClass
828 self.xpathname = xpathname
829 def enum(self):
830 children = []
831 for node in self.getCtx()['global'].getArray(self.obj, self.attr):
832 nodexml = self.heldClass(self, node)
833 children.append(nodexml)
834 return children
835 def matches(self, subexp):
836 return subexp == self.xpathname
837
838class XPathNodeValue(XPathNode):
839 def __init__(self, parent, obj, xpathname):
840 XPathNode.__init__(self, parent, obj, 'val '+xpathname)
841 self.xpathname = xpathname
842 def matches(self, subexp):
843 return subexp == self.xpathname
844
845class XPathNodeHolderVM(XPathNodeHolder):
846 def __init__(self, parent, vbox):
847 XPathNodeHolder.__init__(self, parent, vbox, 'machines', XPathNodeVM, 'vms')
848
849class XPathNodeVM(XPathNode):
850 def __init__(self, parent, obj):
851 XPathNode.__init__(self, parent, obj, 'vm')
852 #def matches(self, subexp):
853 # return subexp=='vm'
854 def enum(self):
855 return [XPathNodeHolderNIC(self, self.obj),
856 XPathNodeValue(self, self.obj.BIOSSettings, 'bios'), ]
857
858class XPathNodeHolderNIC(XPathNodeHolder):
859 def __init__(self, parent, mach):
860 XPathNodeHolder.__init__(self, parent, mach, 'nics', XPathNodeVM, 'nics')
861 self.maxNic = self.getCtx()['vb'].systemProperties.getMaxNetworkAdapters(self.obj.chipsetType)
862 def enum(self):
863 children = []
864 for i in range(0, self.maxNic):
865 node = XPathNodeNIC(self, self.obj.getNetworkAdapter(i))
866 children.append(node)
867 return children
868
869class XPathNodeNIC(XPathNode):
870 def __init__(self, parent, obj):
871 XPathNode.__init__(self, parent, obj, 'nic')
872 def matches(self, subexp):
873 return subexp == 'nic'
874
875class XPathNodeRoot(XPathNode):
876 def __init__(self, ctx):
877 XPathNode.__init__(self, None, None, 'root')
878 self.ctx = ctx
879 def enum(self):
880 return [XPathNodeHolderVM(self, self.ctx['vb'])]
881 def matches(self, subexp):
882 return True
883
884def eval_xpath(ctx, scope):
885 pathnames = scope.split("/")[2:]
886 nodes = [XPathNodeRoot(ctx)]
887 for path in pathnames:
888 seen = []
889 while len(nodes) > 0:
890 node = nodes.pop()
891 seen.append(node)
892 for s in seen:
893 matches = s.lookup(path)
894 for match in matches:
895 nodes.append(match)
896 if len(nodes) == 0:
897 break
898 return nodes
899
900def argsToMach(ctx, args):
901 if len(args) < 2:
902 print("usage: %s [vmname|uuid]" % (args[0]))
903 return None
904 uuid = args[1]
905 mach = machById(ctx, uuid)
906 if mach == None:
907 print("Machine '%s' is unknown, use list command to find available machines" % (uuid))
908 return mach
909
910def helpSingleCmd(cmd, h, sp):
911 if sp != 0:
912 spec = " [ext from "+sp+"]"
913 else:
914 spec = ""
915 print(" %s: %s%s" % (colored(cmd, 'blue'), h, spec))
916
917def helpCmd(_ctx, args):
918 if len(args) == 1:
919 print("Help page:")
920 names = list(commands.keys())
921 names.sort()
922 for i in names:
923 helpSingleCmd(i, commands[i][0], commands[i][2])
924 else:
925 cmd = args[1]
926 c = commands.get(cmd)
927 if c == None:
928 print("Command '%s' not known" % (cmd))
929 else:
930 helpSingleCmd(cmd, c[0], c[2])
931 return 0
932
933def asEnumElem(ctx, enum, elem):
934 enumVals = ctx['const'].all_values(enum)
935 for e in list(enumVals.keys()):
936 if str(elem) == str(enumVals[e]):
937 return colored(e, 'green')
938 return colored("<unknown>", 'green')
939
940def enumFromString(ctx, enum, strg):
941 enumVals = ctx['const'].all_values(enum)
942 return enumVals.get(strg, None)
943
944def listCmd(ctx, _args):
945 for mach in getMachines(ctx, True):
946 try:
947 if mach.teleporterEnabled:
948 tele = "[T] "
949 else:
950 tele = " "
951 print("%sMachine '%s' [%s], machineState=%s, sessionState=%s" % (tele, colVm(ctx, mach.name), mach.id, asEnumElem(ctx, "MachineState", mach.state), asEnumElem(ctx, "SessionState", mach.sessionState)))
952 except Exception as e:
953 printErr(ctx, e)
954 if g_fVerbose:
955 traceback.print_exc()
956 return 0
957
958def infoCmd(ctx, args):
959 if len(args) < 2:
960 print("usage: info [vmname|uuid]")
961 return 0
962 mach = argsToMach(ctx, args)
963 if mach == None:
964 return 0
965 try:
966 vmos = ctx['vb'].getGuestOSType(mach.OSTypeId)
967 except:
968 vmos = None
969 print(" One can use setvar <mach> <var> <value> to change variable, using name in [].")
970 print(" Name [name]: %s" % (colVm(ctx, mach.name)))
971 print(" Description [description]: %s" % (mach.description))
972 print(" ID [n/a]: %s" % (mach.id))
973 print(" OS Type [via OSTypeId]: %s" % (vmos.description if vmos is not None else mach.OSTypeId))
974 print(" Firmware [firmwareType]: %s (%s)" % (asEnumElem(ctx, "FirmwareType", mach.firmwareType), mach.firmwareType))
975 print()
976 print(" CPUs [CPUCount]: %d" % (mach.CPUCount))
977 print(" RAM [memorySize]: %dM" % (mach.memorySize))
978 print(" VRAM [VRAMSize]: %dM" % (mach.graphicsAdapter.VRAMSize))
979 print(" Monitors [monitorCount]: %d" % (mach.graphicsAdapter.monitorCount))
980 print(" Chipset [chipsetType]: %s (%s)" % (asEnumElem(ctx, "ChipsetType", mach.chipsetType), mach.chipsetType))
981 print()
982 print(" Clipboard mode [clipboardMode]: %s (%s)" % (asEnumElem(ctx, "ClipboardMode", mach.clipboardMode), mach.clipboardMode))
983 print(" Machine status [n/a]: %s (%s)" % (asEnumElem(ctx, "SessionState", mach.sessionState), mach.sessionState))
984 print()
985 if mach.teleporterEnabled:
986 print(" Teleport target on port %d (%s)" % (mach.teleporterPort, mach.teleporterPassword))
987 print()
988 bios = mach.BIOSSettings
989 print(" ACPI [BIOSSettings.ACPIEnabled]: %s" % (asState(bios.ACPIEnabled)))
990 print(" APIC [BIOSSettings.IOAPICEnabled]: %s" % (asState(bios.IOAPICEnabled)))
991 hwVirtEnabled = mach.getHWVirtExProperty(ctx['global'].constants.HWVirtExPropertyType_Enabled)
992 print(" Hardware virtualization [guest win machine.setHWVirtExProperty(ctx[\\'const\\'].HWVirtExPropertyType_Enabled, value)]: " + asState(hwVirtEnabled))
993 hwVirtVPID = mach.getHWVirtExProperty(ctx['const'].HWVirtExPropertyType_VPID)
994 print(" VPID support [guest win machine.setHWVirtExProperty(ctx[\\'const\\'].HWVirtExPropertyType_VPID, value)]: " + asState(hwVirtVPID))
995 hwVirtNestedPaging = mach.getHWVirtExProperty(ctx['const'].HWVirtExPropertyType_NestedPaging)
996 print(" Nested paging [guest win machine.setHWVirtExProperty(ctx[\\'const\\'].HWVirtExPropertyType_NestedPaging, value)]: " + asState(hwVirtNestedPaging))
997
998 print(" Hardware 3d acceleration [accelerate3DEnabled]: " + asState(mach.graphicsAdapter.accelerate3DEnabled))
999 print(" Hardware 2d video acceleration [accelerate2DVideoEnabled]: " + asState(mach.graphicsAdapter.accelerate2DVideoEnabled))
1000
1001 print(" Use universal time [RTCUseUTC]: %s" % (asState(mach.RTCUseUTC)))
1002 print(" HPET [HPETEnabled]: %s" % (asState(mach.HPETEnabled)))
1003 if mach.audioAdapter.enabled:
1004 print(" Audio [via audioAdapter]: chip %s; host driver %s" % (asEnumElem(ctx, "AudioControllerType", mach.audioAdapter.audioController), asEnumElem(ctx, "AudioDriverType", mach.audioAdapter.audioDriver)))
1005 print(" CPU hotplugging [CPUHotPlugEnabled]: %s" % (asState(mach.CPUHotPlugEnabled)))
1006
1007 print(" Keyboard [keyboardHIDType]: %s (%s)" % (asEnumElem(ctx, "KeyboardHIDType", mach.keyboardHIDType), mach.keyboardHIDType))
1008 print(" Pointing device [pointingHIDType]: %s (%s)" % (asEnumElem(ctx, "PointingHIDType", mach.pointingHIDType), mach.pointingHIDType))
1009 print(" Last changed [n/a]: " + time.asctime(time.localtime(int(mach.lastStateChange)/1000)))
1010 # OSE has no VRDE
1011 try:
1012 print(" VRDE server [VRDEServer.enabled]: %s" % (asState(mach.VRDEServer.enabled)))
1013 except:
1014 pass
1015
1016 print()
1017 print(colCat(ctx, " USB Controllers:"))
1018 for oUsbCtrl in ctx['global'].getArray(mach, 'USBControllers'):
1019 print(" '%s': type %s standard: %#x" \
1020 % (oUsbCtrl.name, asEnumElem(ctx, "USBControllerType", oUsbCtrl.type), oUsbCtrl.USBStandard))
1021
1022 print()
1023 print(colCat(ctx, " I/O subsystem info:"))
1024 print(" Cache enabled [IOCacheEnabled]: %s" % (asState(mach.IOCacheEnabled)))
1025 print(" Cache size [IOCacheSize]: %dM" % (mach.IOCacheSize))
1026
1027 controllers = ctx['global'].getArray(mach, 'storageControllers')
1028 if controllers:
1029 print()
1030 print(colCat(ctx, " Storage Controllers:"))
1031 for controller in controllers:
1032 print(" '%s': bus %s type %s" % (controller.name, asEnumElem(ctx, "StorageBus", controller.bus), asEnumElem(ctx, "StorageControllerType", controller.controllerType)))
1033
1034 attaches = ctx['global'].getArray(mach, 'mediumAttachments')
1035 if attaches:
1036 print()
1037 print(colCat(ctx, " Media:"))
1038 for a in attaches:
1039 print(" Controller: '%s' port/device: %d:%d type: %s (%s):" % (a.controller, a.port, a.device, asEnumElem(ctx, "DeviceType", a.type), a.type))
1040 medium = a.medium
1041 if a.type == ctx['global'].constants.DeviceType_HardDisk:
1042 print(" HDD:")
1043 print(" Id: %s" % (medium.id))
1044 print(" Location: %s" % (colPath(ctx, medium.location)))
1045 print(" Name: %s" % (medium.name))
1046 print(" Format: %s" % (medium.format))
1047
1048 if a.type == ctx['global'].constants.DeviceType_DVD:
1049 print(" DVD:")
1050 if medium:
1051 print(" Id: %s" % (medium.id))
1052 print(" Name: %s" % (medium.name))
1053 if medium.hostDrive:
1054 print(" Host DVD %s" % (colPath(ctx, medium.location)))
1055 if a.passthrough:
1056 print(" [passthrough mode]")
1057 else:
1058 print(" Virtual image at %s" % (colPath(ctx, medium.location)))
1059 print(" Size: %s" % (medium.size))
1060
1061 if a.type == ctx['global'].constants.DeviceType_Floppy:
1062 print(" Floppy:")
1063 if medium:
1064 print(" Id: %s" % (medium.id))
1065 print(" Name: %s" % (medium.name))
1066 if medium.hostDrive:
1067 print(" Host floppy %s" % (colPath(ctx, medium.location)))
1068 else:
1069 print(" Virtual image at %s" % (colPath(ctx, medium.location)))
1070 print(" Size: %s" % (medium.size))
1071
1072 print()
1073 print(colCat(ctx, " Shared folders:"))
1074 for sf in ctx['global'].getArray(mach, 'sharedFolders'):
1075 printSf(ctx, sf)
1076
1077 return 0
1078
1079def startCmd(ctx, args):
1080 if len(args) < 2:
1081 print("usage: start name <frontend>")
1082 return 0
1083 mach = argsToMach(ctx, args)
1084 if mach == None:
1085 return 0
1086 if len(args) > 2:
1087 vmtype = args[2]
1088 else:
1089 vmtype = "gui"
1090 startVm(ctx, mach, vmtype)
1091 return 0
1092
1093def createVmCmd(ctx, args):
1094 if len(args) != 3:
1095 print("usage: createvm name ostype")
1096 return 0
1097 name = args[1]
1098 oskind = args[2]
1099 try:
1100 ctx['vb'].getGuestOSType(oskind)
1101 except Exception:
1102 print('Unknown OS type:', oskind)
1103 return 0
1104 createVm(ctx, name, oskind)
1105 return 0
1106
1107def ginfoCmd(ctx, args):
1108 if len(args) < 2:
1109 print("usage: ginfo [vmname|uuid]")
1110 return 0
1111 mach = argsToMach(ctx, args)
1112 if mach == None:
1113 return 0
1114 cmdExistingVm(ctx, mach, 'ginfo', '')
1115 return 0
1116
1117def execInGuest(ctx, console, args, env, user, passwd, tmo, inputPipe=None, outputPipe=None):
1118 if len(args) < 1:
1119 print("exec in guest needs at least program name")
1120 return
1121 guest = console.guest
1122 guestSession = guest.createSession(user, passwd, "", "vboxshell guest exec")
1123 # shall contain program name as argv[0]
1124 gargs = args
1125 print("executing %s with args %s as %s" % (args[0], gargs, user))
1126 flags = 0
1127 if inputPipe is not None:
1128 flags = 1 # set WaitForProcessStartOnly
1129 print(args[0])
1130 process = guestSession.processCreate(args[0], gargs, env, [], tmo)
1131 print("executed with pid %d" % (process.PID))
1132 if pid != 0:
1133 try:
1134 while True:
1135 if inputPipe is not None:
1136 indata = inputPipe(ctx)
1137 if indata is not None:
1138 write = len(indata)
1139 off = 0
1140 while write > 0:
1141 w = guest.setProcessInput(pid, 0, 10*1000, indata[off:])
1142 off = off + w
1143 write = write - w
1144 else:
1145 # EOF
1146 try:
1147 guest.setProcessInput(pid, 1, 10*1000, " ")
1148 except:
1149 pass
1150 data = guest.getProcessOutput(pid, 0, 10000, 4096)
1151 if data and len(data) > 0:
1152 sys.stdout.write(data)
1153 continue
1154 progress.waitForCompletion(100)
1155 ctx['global'].waitForEvents(0)
1156 data = guest.getProcessOutput(pid, 0, 0, 4096)
1157 if data and len(data) > 0:
1158 if outputPipe is not None:
1159 outputPipe(ctx, data)
1160 else:
1161 sys.stdout.write(data)
1162 continue
1163 if progress.completed:
1164 break
1165
1166 except KeyboardInterrupt:
1167 print("Interrupted.")
1168 ctx['interrupt'] = True
1169 if progress.cancelable:
1170 progress.cancel()
1171 (_reason, code, _flags) = guest.getProcessStatus(pid)
1172 print("Exit code: %d" % (code))
1173 return 0
1174 else:
1175 reportError(ctx, progress)
1176
1177def copyToGuest(ctx, console, args, user, passwd):
1178 src = args[0]
1179 dst = args[1]
1180 flags = 0
1181 print("Copying host %s to guest %s" % (src, dst))
1182 progress = console.guest.copyToGuest(src, dst, user, passwd, flags)
1183 progressBar(ctx, progress)
1184
1185def nh_raw_input(prompt=""):
1186 stream = sys.stdout
1187 prompt = str(prompt)
1188 if prompt:
1189 stream.write(prompt)
1190 line = sys.stdin.readline()
1191 if not line:
1192 raise EOFError
1193 if line[-1] == '\n':
1194 line = line[:-1]
1195 return line
1196
1197
1198def getCred(_ctx):
1199 import getpass
1200 user = getpass.getuser()
1201 user_inp = nh_raw_input("User (%s): " % (user))
1202 if len(user_inp) > 0:
1203 user = user_inp
1204 passwd = getpass.getpass()
1205
1206 return (user, passwd)
1207
1208def gexecCmd(ctx, args):
1209 if len(args) < 2:
1210 print("usage: gexec [vmname|uuid] command args")
1211 return 0
1212 mach = argsToMach(ctx, args)
1213 if mach == None:
1214 return 0
1215 gargs = args[2:]
1216 env = [] # ["DISPLAY=:0"]
1217 (user, passwd) = getCred(ctx)
1218 gargs.insert(0, lambda ctx, mach, console, args: execInGuest(ctx, console, args, env, user, passwd, 10000))
1219 cmdExistingVm(ctx, mach, 'guestlambda', gargs)
1220 return 0
1221
1222def gcopyCmd(ctx, args):
1223 if len(args) < 2:
1224 print("usage: gcopy [vmname|uuid] host_path guest_path")
1225 return 0
1226 mach = argsToMach(ctx, args)
1227 if mach == None:
1228 return 0
1229 gargs = args[2:]
1230 (user, passwd) = getCred(ctx)
1231 gargs.insert(0, lambda ctx, mach, console, args: copyToGuest(ctx, console, args, user, passwd))
1232 cmdExistingVm(ctx, mach, 'guestlambda', gargs)
1233 return 0
1234
1235def readCmdPipe(ctx, _hcmd):
1236 try:
1237 return ctx['process'].communicate()[0]
1238 except:
1239 return None
1240
1241def gpipeCmd(ctx, args):
1242 if len(args) < 4:
1243 print("usage: gpipe [vmname|uuid] hostProgram guestProgram, such as gpipe linux '/bin/uname -a' '/bin/sh -c \"/usr/bin/tee; /bin/uname -a\"'")
1244 return 0
1245 mach = argsToMach(ctx, args)
1246 if mach == None:
1247 return 0
1248 hcmd = args[2]
1249 gcmd = args[3]
1250 (user, passwd) = getCred(ctx)
1251 import subprocess
1252 ctx['process'] = subprocess.Popen(split_no_quotes(hcmd), stdout=subprocess.PIPE)
1253 gargs = split_no_quotes(gcmd)
1254 env = []
1255 gargs.insert(0, lambda ctx, mach, console, args: execInGuest(ctx, console, args, env, user, passwd, 10000, lambda ctx:readCmdPipe(ctx, hcmd)))
1256 cmdExistingVm(ctx, mach, 'guestlambda', gargs)
1257 try:
1258 ctx['process'].terminate()
1259 except:
1260 pass
1261 ctx['process'] = None
1262 return 0
1263
1264
1265def removeVmCmd(ctx, args):
1266 mach = argsToMach(ctx, args)
1267 if mach == None:
1268 return 0
1269 removeVm(ctx, mach)
1270 return 0
1271
1272def pauseCmd(ctx, args):
1273 mach = argsToMach(ctx, args)
1274 if mach == None:
1275 return 0
1276 cmdExistingVm(ctx, mach, 'pause', '')
1277 return 0
1278
1279def powerdownCmd(ctx, args):
1280 mach = argsToMach(ctx, args)
1281 if mach == None:
1282 return 0
1283 cmdExistingVm(ctx, mach, 'powerdown', '')
1284 return 0
1285
1286def powerbuttonCmd(ctx, args):
1287 mach = argsToMach(ctx, args)
1288 if mach == None:
1289 return 0
1290 cmdExistingVm(ctx, mach, 'powerbutton', '')
1291 return 0
1292
1293def resumeCmd(ctx, args):
1294 mach = argsToMach(ctx, args)
1295 if mach == None:
1296 return 0
1297 cmdExistingVm(ctx, mach, 'resume', '')
1298 return 0
1299
1300def saveCmd(ctx, args):
1301 mach = argsToMach(ctx, args)
1302 if mach == None:
1303 return 0
1304 cmdExistingVm(ctx, mach, 'save', '')
1305 return 0
1306
1307def statsCmd(ctx, args):
1308 mach = argsToMach(ctx, args)
1309 if mach == None:
1310 return 0
1311 cmdExistingVm(ctx, mach, 'stats', '')
1312 return 0
1313
1314def guestCmd(ctx, args):
1315 if len(args) < 3:
1316 print("usage: guest name commands")
1317 return 0
1318 mach = argsToMach(ctx, args)
1319 if mach == None:
1320 return 0
1321 if mach.state != ctx['const'].MachineState_Running:
1322 cmdClosedVm(ctx, mach, lambda ctx, mach, a: guestExec (ctx, mach, None, ' '.join(args[2:])))
1323 else:
1324 cmdExistingVm(ctx, mach, 'guest', ' '.join(args[2:]))
1325 return 0
1326
1327def screenshotCmd(ctx, args):
1328 if len(args) < 2:
1329 print("usage: screenshot vm <file> <width> <height> <monitor>")
1330 return 0
1331 mach = argsToMach(ctx, args)
1332 if mach == None:
1333 return 0
1334 cmdExistingVm(ctx, mach, 'screenshot', args[2:])
1335 return 0
1336
1337def teleportCmd(ctx, args):
1338 if len(args) < 3:
1339 print("usage: teleport name host:port <password>")
1340 return 0
1341 mach = argsToMach(ctx, args)
1342 if mach == None:
1343 return 0
1344 cmdExistingVm(ctx, mach, 'teleport', args[2:])
1345 return 0
1346
1347def portalsettings(_ctx, mach, args):
1348 enabled = args[0]
1349 mach.teleporterEnabled = enabled
1350 if enabled:
1351 port = args[1]
1352 passwd = args[2]
1353 mach.teleporterPort = port
1354 mach.teleporterPassword = passwd
1355
1356def openportalCmd(ctx, args):
1357 if len(args) < 3:
1358 print("usage: openportal name port <password>")
1359 return 0
1360 mach = argsToMach(ctx, args)
1361 if mach == None:
1362 return 0
1363 port = int(args[2])
1364 if len(args) > 3:
1365 passwd = args[3]
1366 else:
1367 passwd = ""
1368 if not mach.teleporterEnabled or mach.teleporterPort != port or passwd:
1369 cmdClosedVm(ctx, mach, portalsettings, [True, port, passwd])
1370 startVm(ctx, mach, "gui")
1371 return 0
1372
1373def closeportalCmd(ctx, args):
1374 if len(args) < 2:
1375 print("usage: closeportal name")
1376 return 0
1377 mach = argsToMach(ctx, args)
1378 if mach == None:
1379 return 0
1380 if mach.teleporterEnabled:
1381 cmdClosedVm(ctx, mach, portalsettings, [False])
1382 return 0
1383
1384def gueststatsCmd(ctx, args):
1385 if len(args) < 2:
1386 print("usage: gueststats name <check interval>")
1387 return 0
1388 mach = argsToMach(ctx, args)
1389 if mach == None:
1390 return 0
1391 cmdExistingVm(ctx, mach, 'gueststats', args[2:])
1392 return 0
1393
1394def plugcpu(_ctx, mach, args):
1395 plug = args[0]
1396 cpu = args[1]
1397 if plug:
1398 print("Adding CPU %d..." % (cpu))
1399 mach.hotPlugCPU(cpu)
1400 else:
1401 print("Removing CPU %d..." % (cpu))
1402 mach.hotUnplugCPU(cpu)
1403
1404def plugcpuCmd(ctx, args):
1405 if len(args) < 2:
1406 print("usage: plugcpu name cpuid")
1407 return 0
1408 mach = argsToMach(ctx, args)
1409 if mach == None:
1410 return 0
1411 if str(mach.sessionState) != str(ctx['const'].SessionState_Locked):
1412 if mach.CPUHotPlugEnabled:
1413 cmdClosedVm(ctx, mach, plugcpu, [True, int(args[2])])
1414 else:
1415 cmdExistingVm(ctx, mach, 'plugcpu', args[2])
1416 return 0
1417
1418def unplugcpuCmd(ctx, args):
1419 if len(args) < 2:
1420 print("usage: unplugcpu name cpuid")
1421 return 0
1422 mach = argsToMach(ctx, args)
1423 if mach == None:
1424 return 0
1425 if str(mach.sessionState) != str(ctx['const'].SessionState_Locked):
1426 if mach.CPUHotPlugEnabled:
1427 cmdClosedVm(ctx, mach, plugcpu, [False, int(args[2])])
1428 else:
1429 cmdExistingVm(ctx, mach, 'unplugcpu', args[2])
1430 return 0
1431
1432def setvar(_ctx, _mach, args):
1433 expr = 'mach.'+args[0]+' = '+args[1]
1434 print("Executing", expr)
1435 exec(expr)
1436
1437def setvarCmd(ctx, args):
1438 if len(args) < 4:
1439 print("usage: setvar [vmname|uuid] expr value")
1440 return 0
1441 mach = argsToMach(ctx, args)
1442 if mach == None:
1443 return 0
1444 cmdClosedVm(ctx, mach, setvar, args[2:])
1445 return 0
1446
1447def setvmextra(_ctx, mach, args):
1448 key = args[0]
1449 value = args[1]
1450 print("%s: setting %s to %s" % (mach.name, key, value if value else None))
1451 mach.setExtraData(key, value)
1452
1453def setExtraDataCmd(ctx, args):
1454 if len(args) < 3:
1455 print("usage: setextra [vmname|uuid|global] key <value>")
1456 return 0
1457 key = args[2]
1458 if len(args) == 4:
1459 value = args[3]
1460 else:
1461 value = ''
1462 if args[1] == 'global':
1463 ctx['vb'].setExtraData(key, value)
1464 return 0
1465
1466 mach = argsToMach(ctx, args)
1467 if mach == None:
1468 return 0
1469 cmdClosedVm(ctx, mach, setvmextra, [key, value])
1470 return 0
1471
1472def printExtraKey(obj, key, value):
1473 print("%s: '%s' = '%s'" % (obj, key, value))
1474
1475def getExtraDataCmd(ctx, args):
1476 if len(args) < 2:
1477 print("usage: getextra [vmname|uuid|global] <key>")
1478 return 0
1479 if len(args) == 3:
1480 key = args[2]
1481 else:
1482 key = None
1483
1484 if args[1] == 'global':
1485 obj = ctx['vb']
1486 else:
1487 obj = argsToMach(ctx, args)
1488 if obj == None:
1489 return 0
1490
1491 if key == None:
1492 keys = obj.getExtraDataKeys()
1493 else:
1494 keys = [ key ]
1495 for k in keys:
1496 printExtraKey(args[1], k, obj.getExtraData(k))
1497
1498 return 0
1499
1500def quitCmd(_ctx, _args):
1501 return 1
1502
1503def aliasCmd(ctx, args):
1504 if len(args) == 3:
1505 aliases[args[1]] = args[2]
1506 return 0
1507
1508 for (key, value) in list(aliases.items()):
1509 print("'%s' is an alias for '%s'" % (key, value))
1510 return 0
1511
1512def verboseCmd(ctx, args):
1513 global g_fVerbose
1514 if len(args) > 1:
1515 g_fVerbose = (args[1]=='on')
1516 else:
1517 g_fVerbose = not g_fVerbose
1518 return 0
1519
1520def colorsCmd(ctx, args):
1521 global g_fHasColors
1522 if len(args) > 1:
1523 g_fHasColors = (args[1] == 'on')
1524 else:
1525 g_fHasColors = not g_fHasColors
1526 return 0
1527
1528def hostCmd(ctx, args):
1529 vbox = ctx['vb']
1530 try:
1531 print("VirtualBox version %s" % (colored(vbox.version, 'blue')))
1532 except Exception as e:
1533 printErr(ctx, e)
1534 if g_fVerbose:
1535 traceback.print_exc()
1536 props = vbox.systemProperties
1537 print("Machines: %s" % (colPath(ctx, props.defaultMachineFolder)))
1538
1539 #print("Global shared folders:")
1540 #for ud in ctx['global'].getArray(vbox, 'sharedFolders'):
1541 # printSf(ctx, sf)
1542 host = vbox.host
1543 cnt = host.processorCount
1544 print(colCat(ctx, "Processors:"))
1545 print(" available/online: %d/%d " % (cnt, host.processorOnlineCount))
1546 for i in range(0, cnt):
1547 print(" processor #%d speed: %dMHz %s" % (i, host.getProcessorSpeed(i), host.getProcessorDescription(i)))
1548
1549 print(colCat(ctx, "RAM:"))
1550 print(" %dM (free %dM)" % (host.memorySize, host.memoryAvailable))
1551 print(colCat(ctx, "OS:"))
1552 print(" %s (%s)" % (host.operatingSystem, host.OSVersion))
1553 if host.acceleration3DAvailable:
1554 print(colCat(ctx, "3D acceleration available"))
1555 else:
1556 print(colCat(ctx, "3D acceleration NOT available"))
1557
1558 print(colCat(ctx, "Network interfaces:"))
1559 for ni in ctx['global'].getArray(host, 'networkInterfaces'):
1560 print(" %s (%s)" % (ni.name, ni.IPAddress))
1561
1562 print(colCat(ctx, "DVD drives:"))
1563 for dd in ctx['global'].getArray(host, 'DVDDrives'):
1564 print(" %s - %s" % (dd.name, dd.description))
1565
1566 print(colCat(ctx, "Floppy drives:"))
1567 for dd in ctx['global'].getArray(host, 'floppyDrives'):
1568 print(" %s - %s" % (dd.name, dd.description))
1569
1570 print(colCat(ctx, "USB devices:"))
1571 for ud in ctx['global'].getArray(host, 'USBDevices'):
1572 printHostUsbDev(ctx, ud)
1573
1574 if ctx['perf']:
1575 for metric in ctx['perf'].query(["*"], [host]):
1576 print(metric['name'], metric['values_as_string'])
1577
1578 return 0
1579
1580def monitorGuestCmd(ctx, args):
1581 if len(args) < 2:
1582 print("usage: monitorGuest name (duration)")
1583 return 0
1584 mach = argsToMach(ctx, args)
1585 if mach == None:
1586 return 0
1587 dur = 5
1588 if len(args) > 2:
1589 dur = float(args[2])
1590 active = False
1591 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.eventSource, active, dur)])
1592 return 0
1593
1594def monitorGuestKbdCmd(ctx, args):
1595 if len(args) < 2:
1596 print("usage: monitorGuestKbd name (duration)")
1597 return 0
1598 mach = argsToMach(ctx, args)
1599 if mach == None:
1600 return 0
1601 dur = 5
1602 if len(args) > 2:
1603 dur = float(args[2])
1604 active = False
1605 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.keyboard.eventSource, active, dur)])
1606 return 0
1607
1608def monitorGuestMouseCmd(ctx, args):
1609 if len(args) < 2:
1610 print("usage: monitorGuestMouse name (duration)")
1611 return 0
1612 mach = argsToMach(ctx, args)
1613 if mach == None:
1614 return 0
1615 dur = 5
1616 if len(args) > 2:
1617 dur = float(args[2])
1618 active = False
1619 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.mouse.eventSource, active, dur)])
1620 return 0
1621
1622def monitorGuestMultiTouchCmd(ctx, args):
1623 if len(args) < 2:
1624 print("usage: monitorGuestMultiTouch name (duration)")
1625 return 0
1626 mach = argsToMach(ctx, args)
1627 if mach == None:
1628 return 0
1629 dur = 5
1630 if len(args) > 2:
1631 dur = float(args[2])
1632 active = False
1633 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.mouse.eventSource, active, dur)])
1634 return 0
1635
1636def monitorVBoxCmd(ctx, args):
1637 if len(args) > 2:
1638 print("usage: monitorVBox (duration)")
1639 return 0
1640 dur = 5
1641 if len(args) > 1:
1642 dur = float(args[1])
1643 vbox = ctx['vb']
1644 active = False
1645 monitorSource(ctx, vbox.eventSource, active, dur)
1646 return 0
1647
1648def getAdapterType(ctx, natype):
1649 if (natype == ctx['global'].constants.NetworkAdapterType_Am79C970A or
1650 natype == ctx['global'].constants.NetworkAdapterType_Am79C973 or
1651 natype == ctx['global'].constants.NetworkAdapterType_Am79C960):
1652 return "pcnet"
1653 elif (natype == ctx['global'].constants.NetworkAdapterType_I82540EM or
1654 natype == ctx['global'].constants.NetworkAdapterType_I82545EM or
1655 natype == ctx['global'].constants.NetworkAdapterType_I82543GC):
1656 return "e1000"
1657 elif (natype == ctx['global'].constants.NetworkAdapterType_Virtio):
1658 return "virtio"
1659 elif (natype == ctx['global'].constants.NetworkAdapterType_Virtio_1_0):
1660 return "virtio_1.0"
1661 elif (natype == ctx['global'].constants.NetworkAdapterType_Null):
1662 return None
1663 else:
1664 raise Exception("Unknown adapter type: "+natype)
1665
1666
1667def portForwardCmd(ctx, args):
1668 if len(args) != 5:
1669 print("usage: portForward <vm> <adapter> <hostPort> <guestPort>")
1670 return 0
1671 mach = argsToMach(ctx, args)
1672 if mach == None:
1673 return 0
1674 adapterNum = int(args[2])
1675 hostPort = int(args[3])
1676 guestPort = int(args[4])
1677 proto = "TCP"
1678 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
1679 mach = session.machine
1680
1681 adapter = mach.getNetworkAdapter(adapterNum)
1682 adapterType = getAdapterType(ctx, adapter.adapterType)
1683
1684 profile_name = proto+"_"+str(hostPort)+"_"+str(guestPort)
1685 config = "VBoxInternal/Devices/" + adapterType + "/"
1686 config = config + str(adapter.slot) +"/LUN#0/Config/" + profile_name
1687
1688 mach.setExtraData(config + "/Protocol", proto)
1689 mach.setExtraData(config + "/HostPort", str(hostPort))
1690 mach.setExtraData(config + "/GuestPort", str(guestPort))
1691
1692 mach.saveSettings()
1693 session.unlockMachine()
1694
1695 return 0
1696
1697
1698def showLogCmd(ctx, args):
1699 if len(args) < 2:
1700 print("usage: showLog vm <num>")
1701 return 0
1702 mach = argsToMach(ctx, args)
1703 if mach == None:
1704 return 0
1705
1706 log = 0
1707 if len(args) > 2:
1708 log = args[2]
1709
1710 uOffset = 0
1711 while True:
1712 data = mach.readLog(log, uOffset, 4096)
1713 if len(data) == 0:
1714 break
1715 # print adds either NL or space to chunks not ending with a NL
1716 sys.stdout.write(str(data))
1717 uOffset += len(data)
1718
1719 return 0
1720
1721def findLogCmd(ctx, args):
1722 if len(args) < 3:
1723 print("usage: findLog vm pattern <num>")
1724 return 0
1725 mach = argsToMach(ctx, args)
1726 if mach == None:
1727 return 0
1728
1729 log = 0
1730 if len(args) > 3:
1731 log = args[3]
1732
1733 pattern = args[2]
1734 uOffset = 0
1735 while True:
1736 # to reduce line splits on buffer boundary
1737 data = mach.readLog(log, uOffset, 512*1024)
1738 if len(data) == 0:
1739 break
1740 d = str(data).split("\n")
1741 for s in d:
1742 match = re.findall(pattern, s)
1743 if len(match) > 0:
1744 for mt in match:
1745 s = s.replace(mt, colored(mt, 'red'))
1746 print(s)
1747 uOffset += len(data)
1748
1749 return 0
1750
1751
1752def findAssertCmd(ctx, args):
1753 if len(args) < 2:
1754 print("usage: findAssert vm <num>")
1755 return 0
1756 mach = argsToMach(ctx, args)
1757 if mach == None:
1758 return 0
1759
1760 log = 0
1761 if len(args) > 2:
1762 log = args[2]
1763
1764 uOffset = 0
1765 ere = re.compile(r'(Expression:|\!\!\!\!\!\!)')
1766 active = False
1767 context = 0
1768 while True:
1769 # to reduce line splits on buffer boundary
1770 data = mach.readLog(log, uOffset, 512*1024)
1771 if len(data) == 0:
1772 break
1773 d = str(data).split("\n")
1774 for s in d:
1775 if active:
1776 print(s)
1777 if context == 0:
1778 active = False
1779 else:
1780 context = context - 1
1781 continue
1782 match = ere.findall(s)
1783 if len(match) > 0:
1784 active = True
1785 context = 50
1786 print(s)
1787 uOffset += len(data)
1788
1789 return 0
1790
1791def evalCmd(ctx, args):
1792 expr = ' '.join(args[1:])
1793 try:
1794 exec(expr)
1795 except Exception as e:
1796 printErr(ctx, e)
1797 if g_fVerbose:
1798 traceback.print_exc()
1799 return 0
1800
1801def reloadExtCmd(ctx, args):
1802 # maybe will want more args smartness
1803 checkUserExtensions(ctx, commands, getHomeFolder(ctx))
1804 autoCompletion(commands, ctx)
1805 return 0
1806
1807def runScriptCmd(ctx, args):
1808 if len(args) != 2:
1809 print("usage: runScript <script>")
1810 return 0
1811 try:
1812 lf = open(args[1], 'r')
1813 except IOError as e:
1814 print("cannot open:", args[1], ":", e)
1815 return 0
1816
1817 try:
1818 lines = lf.readlines()
1819 ctx['scriptLine'] = 0
1820 ctx['interrupt'] = False
1821 while ctx['scriptLine'] < len(lines):
1822 line = lines[ctx['scriptLine']]
1823 ctx['scriptLine'] = ctx['scriptLine'] + 1
1824 done = runCommand(ctx, line)
1825 if done != 0 or ctx['interrupt']:
1826 break
1827
1828 except Exception as e:
1829 printErr(ctx, e)
1830 if g_fVerbose:
1831 traceback.print_exc()
1832 lf.close()
1833 return 0
1834
1835def sleepCmd(ctx, args):
1836 if len(args) != 2:
1837 print("usage: sleep <secs>")
1838 return 0
1839
1840 try:
1841 time.sleep(float(args[1]))
1842 except:
1843 # to allow sleep interrupt
1844 pass
1845 return 0
1846
1847
1848def shellCmd(ctx, args):
1849 if len(args) < 2:
1850 print("usage: shell <commands>")
1851 return 0
1852 cmd = ' '.join(args[1:])
1853
1854 try:
1855 os.system(cmd)
1856 except KeyboardInterrupt:
1857 # to allow shell command interruption
1858 pass
1859 return 0
1860
1861
1862def connectCmd(ctx, args):
1863 if len(args) > 4:
1864 print("usage: connect url <username> <passwd>")
1865 return 0
1866
1867 if ctx['vb'] is not None:
1868 print("Already connected, disconnect first...")
1869 return 0
1870
1871 if len(args) > 1:
1872 url = args[1]
1873 else:
1874 url = None
1875
1876 if len(args) > 2:
1877 user = args[2]
1878 else:
1879 user = ""
1880
1881 if len(args) > 3:
1882 passwd = args[3]
1883 else:
1884 passwd = ""
1885
1886 ctx['wsinfo'] = [url, user, passwd]
1887 ctx['vb'] = ctx['global'].platform.connect(url, user, passwd)
1888 try:
1889 print("Running VirtualBox version %s" % (ctx['vb'].version))
1890 except Exception as e:
1891 printErr(ctx, e)
1892 if g_fVerbose:
1893 traceback.print_exc()
1894 ctx['perf'] = ctx['global'].getPerfCollector(ctx['vb'])
1895 return 0
1896
1897def disconnectCmd(ctx, args):
1898 if len(args) != 1:
1899 print("usage: disconnect")
1900 return 0
1901
1902 if ctx['vb'] is None:
1903 print("Not connected yet.")
1904 return 0
1905
1906 try:
1907 ctx['global'].platform.disconnect()
1908 except:
1909 ctx['vb'] = None
1910 raise
1911
1912 ctx['vb'] = None
1913 return 0
1914
1915def reconnectCmd(ctx, args):
1916 if ctx['wsinfo'] is None:
1917 print("Never connected...")
1918 return 0
1919
1920 try:
1921 ctx['global'].platform.disconnect()
1922 except:
1923 pass
1924
1925 [url, user, passwd] = ctx['wsinfo']
1926 ctx['vb'] = ctx['global'].platform.connect(url, user, passwd)
1927 try:
1928 print("Running VirtualBox version %s" % (ctx['vb'].version))
1929 except Exception as e:
1930 printErr(ctx, e)
1931 if g_fVerbose:
1932 traceback.print_exc()
1933 ctx['perf'] = ctx['global'].getPerfCollector(ctx['vb'])
1934 return 0
1935
1936def exportVMCmd(ctx, args):
1937 if len(args) < 3:
1938 print("usage: exportVm <machine> <path> <format> <license>")
1939 return 0
1940 mach = argsToMach(ctx, args)
1941 if mach is None:
1942 return 0
1943 path = args[2]
1944 if len(args) > 3:
1945 fmt = args[3]
1946 else:
1947 fmt = "ovf-1.0"
1948 if len(args) > 4:
1949 lic = args[4]
1950 else:
1951 lic = "GPL"
1952
1953 app = ctx['vb'].createAppliance()
1954 desc = mach.export(app)
1955 desc.addDescription(ctx['global'].constants.VirtualSystemDescriptionType_License, lic, "")
1956 progress = app.write(fmt, path)
1957 if (progressBar(ctx, progress) and int(progress.resultCode) == 0):
1958 print("Exported to %s in format %s" % (path, fmt))
1959 else:
1960 reportError(ctx, progress)
1961 return 0
1962
1963# PC XT scancodes
1964scancodes = {
1965 'a': 0x1e,
1966 'b': 0x30,
1967 'c': 0x2e,
1968 'd': 0x20,
1969 'e': 0x12,
1970 'f': 0x21,
1971 'g': 0x22,
1972 'h': 0x23,
1973 'i': 0x17,
1974 'j': 0x24,
1975 'k': 0x25,
1976 'l': 0x26,
1977 'm': 0x32,
1978 'n': 0x31,
1979 'o': 0x18,
1980 'p': 0x19,
1981 'q': 0x10,
1982 'r': 0x13,
1983 's': 0x1f,
1984 't': 0x14,
1985 'u': 0x16,
1986 'v': 0x2f,
1987 'w': 0x11,
1988 'x': 0x2d,
1989 'y': 0x15,
1990 'z': 0x2c,
1991 '0': 0x0b,
1992 '1': 0x02,
1993 '2': 0x03,
1994 '3': 0x04,
1995 '4': 0x05,
1996 '5': 0x06,
1997 '6': 0x07,
1998 '7': 0x08,
1999 '8': 0x09,
2000 '9': 0x0a,
2001 ' ': 0x39,
2002 '-': 0xc,
2003 '=': 0xd,
2004 '[': 0x1a,
2005 ']': 0x1b,
2006 ';': 0x27,
2007 '\'': 0x28,
2008 ',': 0x33,
2009 '.': 0x34,
2010 '/': 0x35,
2011 '\t': 0xf,
2012 '\n': 0x1c,
2013 '`': 0x29
2014}
2015
2016extScancodes = {
2017 'ESC' : [0x01],
2018 'BKSP': [0xe],
2019 'SPACE': [0x39],
2020 'TAB': [0x0f],
2021 'CAPS': [0x3a],
2022 'ENTER': [0x1c],
2023 'LSHIFT': [0x2a],
2024 'RSHIFT': [0x36],
2025 'INS': [0xe0, 0x52],
2026 'DEL': [0xe0, 0x53],
2027 'END': [0xe0, 0x4f],
2028 'HOME': [0xe0, 0x47],
2029 'PGUP': [0xe0, 0x49],
2030 'PGDOWN': [0xe0, 0x51],
2031 'LGUI': [0xe0, 0x5b], # GUI, aka Win, aka Apple key
2032 'RGUI': [0xe0, 0x5c],
2033 'LCTR': [0x1d],
2034 'RCTR': [0xe0, 0x1d],
2035 'LALT': [0x38],
2036 'RALT': [0xe0, 0x38],
2037 'APPS': [0xe0, 0x5d],
2038 'F1': [0x3b],
2039 'F2': [0x3c],
2040 'F3': [0x3d],
2041 'F4': [0x3e],
2042 'F5': [0x3f],
2043 'F6': [0x40],
2044 'F7': [0x41],
2045 'F8': [0x42],
2046 'F9': [0x43],
2047 'F10': [0x44 ],
2048 'F11': [0x57],
2049 'F12': [0x58],
2050 'UP': [0xe0, 0x48],
2051 'LEFT': [0xe0, 0x4b],
2052 'DOWN': [0xe0, 0x50],
2053 'RIGHT': [0xe0, 0x4d],
2054}
2055
2056def keyDown(ch):
2057 code = scancodes.get(ch, 0x0)
2058 if code != 0:
2059 return [code]
2060 extCode = extScancodes.get(ch, [])
2061 if len(extCode) == 0:
2062 print("bad ext", ch)
2063 return extCode
2064
2065def keyUp(ch):
2066 codes = keyDown(ch)[:] # make a copy
2067 if len(codes) > 0:
2068 codes[len(codes)-1] += 0x80
2069 return codes
2070
2071def typeInGuest(console, text, delay):
2072 pressed = []
2073 group = False
2074 modGroupEnd = True
2075 i = 0
2076 kbd = console.keyboard
2077 while i < len(text):
2078 ch = text[i]
2079 i = i+1
2080 if ch == '{':
2081 # start group, all keys to be pressed at the same time
2082 group = True
2083 continue
2084 if ch == '}':
2085 # end group, release all keys
2086 for c in pressed:
2087 kbd.putScancodes(keyUp(c))
2088 pressed = []
2089 group = False
2090 continue
2091 if ch == 'W':
2092 # just wait a bit
2093 time.sleep(0.3)
2094 continue
2095 if ch == '^' or ch == '|' or ch == '$' or ch == '_':
2096 if ch == '^':
2097 ch = 'LCTR'
2098 if ch == '|':
2099 ch = 'LSHIFT'
2100 if ch == '_':
2101 ch = 'LALT'
2102 if ch == '$':
2103 ch = 'LGUI'
2104 if not group:
2105 modGroupEnd = False
2106 else:
2107 if ch == '\\':
2108 if i < len(text):
2109 ch = text[i]
2110 i = i+1
2111 if ch == 'n':
2112 ch = '\n'
2113 elif ch == '&':
2114 combo = ""
2115 while i < len(text):
2116 ch = text[i]
2117 i = i+1
2118 if ch == ';':
2119 break
2120 combo += ch
2121 ch = combo
2122 modGroupEnd = True
2123 kbd.putScancodes(keyDown(ch))
2124 pressed.insert(0, ch)
2125 if not group and modGroupEnd:
2126 for c in pressed:
2127 kbd.putScancodes(keyUp(c))
2128 pressed = []
2129 modGroupEnd = True
2130 time.sleep(delay)
2131
2132def typeGuestCmd(ctx, args):
2133 if len(args) < 3:
2134 print("usage: typeGuest <machine> <text> <charDelay>")
2135 return 0
2136 mach = argsToMach(ctx, args)
2137 if mach is None:
2138 return 0
2139
2140 text = args[2]
2141
2142 if len(args) > 3:
2143 delay = float(args[3])
2144 else:
2145 delay = 0.1
2146
2147 gargs = [lambda ctx, mach, console, args: typeInGuest(console, text, delay)]
2148 cmdExistingVm(ctx, mach, 'guestlambda', gargs)
2149
2150 return 0
2151
2152def optId(verbose, uuid):
2153 if verbose:
2154 return ": "+uuid
2155 else:
2156 return ""
2157
2158def asSize(val, inBytes):
2159 if inBytes:
2160 return int(val)/(1024*1024)
2161 else:
2162 return int(val)
2163
2164def listMediaCmd(ctx, args):
2165 if len(args) > 1:
2166 verbose = int(args[1])
2167 else:
2168 verbose = False
2169 hdds = ctx['global'].getArray(ctx['vb'], 'hardDisks')
2170 print(colCat(ctx, "Hard disks:"))
2171 for hdd in hdds:
2172 if hdd.state != ctx['global'].constants.MediumState_Created:
2173 hdd.refreshState()
2174 print(" %s (%s)%s %s [logical %s]" % (colPath(ctx, hdd.location), hdd.format, optId(verbose, hdd.id), colSizeM(ctx, asSize(hdd.size, True)), colSizeM(ctx, asSize(hdd.logicalSize, True))))
2175
2176 dvds = ctx['global'].getArray(ctx['vb'], 'DVDImages')
2177 print(colCat(ctx, "CD/DVD disks:"))
2178 for dvd in dvds:
2179 if dvd.state != ctx['global'].constants.MediumState_Created:
2180 dvd.refreshState()
2181 print(" %s (%s)%s %s" % (colPath(ctx, dvd.location), dvd.format, optId(verbose, dvd.id), colSizeM(ctx, asSize(dvd.size, True))))
2182
2183 floppys = ctx['global'].getArray(ctx['vb'], 'floppyImages')
2184 print(colCat(ctx, "Floppy disks:"))
2185 for floppy in floppys:
2186 if floppy.state != ctx['global'].constants.MediumState_Created:
2187 floppy.refreshState()
2188 print(" %s (%s)%s %s" % (colPath(ctx, floppy.location), floppy.format, optId(verbose, floppy.id), colSizeM(ctx, asSize(floppy.size, True))))
2189
2190 return 0
2191
2192def listUsbCmd(ctx, args):
2193 if len(args) > 1:
2194 print("usage: listUsb")
2195 return 0
2196
2197 host = ctx['vb'].host
2198 for ud in ctx['global'].getArray(host, 'USBDevices'):
2199 printHostUsbDev(ctx, ud)
2200
2201 return 0
2202
2203def findDevOfType(ctx, mach, devtype):
2204 atts = ctx['global'].getArray(mach, 'mediumAttachments')
2205 for a in atts:
2206 if a.type == devtype:
2207 return [a.controller, a.port, a.device]
2208 return [None, 0, 0]
2209
2210def createHddCmd(ctx, args):
2211 if len(args) < 3:
2212 print("usage: createHdd sizeM location type")
2213 return 0
2214
2215 size = int(args[1])
2216 loc = args[2]
2217 if len(args) > 3:
2218 fmt = args[3]
2219 else:
2220 fmt = "vdi"
2221
2222 hdd = ctx['vb'].createMedium(fmt, loc, ctx['global'].constants.AccessMode_ReadWrite, ctx['global'].constants.DeviceType_HardDisk)
2223 progress = hdd.createBaseStorage(size, (ctx['global'].constants.MediumVariant_Standard, ))
2224 if progressBar(ctx,progress) and hdd.id:
2225 print("created HDD at %s as %s" % (colPath(ctx,hdd.location), hdd.id))
2226 else:
2227 print("cannot create disk (file %s exist?)" % (loc))
2228 reportError(ctx,progress)
2229 return 0
2230
2231 return 0
2232
2233def registerHddCmd(ctx, args):
2234 if len(args) < 2:
2235 print("usage: registerHdd location")
2236 return 0
2237
2238 vbox = ctx['vb']
2239 loc = args[1]
2240 setImageId = False
2241 imageId = ""
2242 setParentId = False
2243 parentId = ""
2244 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2245 print("registered HDD as %s" % (hdd.id))
2246 return 0
2247
2248def controldevice(ctx, mach, args):
2249 [ctr, port, slot, devtype, uuid] = args
2250 mach.attachDevice(ctr, port, slot, devtype, uuid)
2251
2252def attachHddCmd(ctx, args):
2253 if len(args) < 3:
2254 print("usage: attachHdd vm hdd controller port:slot")
2255 return 0
2256
2257 mach = argsToMach(ctx, args)
2258 if mach is None:
2259 return 0
2260 vbox = ctx['vb']
2261 loc = args[2]
2262 try:
2263 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2264 except:
2265 print("no HDD with path %s registered" % (loc))
2266 return 0
2267 if len(args) > 3:
2268 ctr = args[3]
2269 (port, slot) = args[4].split(":")
2270 else:
2271 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_HardDisk)
2272
2273 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.attachDevice(ctr, port, slot, ctx['global'].constants.DeviceType_HardDisk, hdd.id))
2274 return 0
2275
2276def detachVmDevice(ctx, mach, args):
2277 atts = ctx['global'].getArray(mach, 'mediumAttachments')
2278 hid = args[0]
2279 for a in atts:
2280 if a.medium:
2281 if hid == "ALL" or a.medium.id == hid:
2282 mach.detachDevice(a.controller, a.port, a.device)
2283
2284def detachMedium(ctx, mid, medium):
2285 cmdClosedVm(ctx, machById(ctx, mid), detachVmDevice, [medium])
2286
2287def detachHddCmd(ctx, args):
2288 if len(args) < 3:
2289 print("usage: detachHdd vm hdd")
2290 return 0
2291
2292 mach = argsToMach(ctx, args)
2293 if mach is None:
2294 return 0
2295 vbox = ctx['vb']
2296 loc = args[2]
2297 try:
2298 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2299 except:
2300 print("no HDD with path %s registered" % (loc))
2301 return 0
2302
2303 detachMedium(ctx, mach.id, hdd)
2304 return 0
2305
2306def unregisterHddCmd(ctx, args):
2307 if len(args) < 2:
2308 print("usage: unregisterHdd path <vmunreg>")
2309 return 0
2310
2311 vbox = ctx['vb']
2312 loc = args[1]
2313 if len(args) > 2:
2314 vmunreg = int(args[2])
2315 else:
2316 vmunreg = 0
2317 try:
2318 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2319 except:
2320 print("no HDD with path %s registered" % (loc))
2321 return 0
2322
2323 if vmunreg != 0:
2324 machs = ctx['global'].getArray(hdd, 'machineIds')
2325 try:
2326 for mach in machs:
2327 print("Trying to detach from %s" % (mach))
2328 detachMedium(ctx, mach, hdd)
2329 except Exception as e:
2330 print('failed: ', e)
2331 return 0
2332 hdd.close()
2333 return 0
2334
2335def removeHddCmd(ctx, args):
2336 if len(args) != 2:
2337 print("usage: removeHdd path")
2338 return 0
2339
2340 vbox = ctx['vb']
2341 loc = args[1]
2342 try:
2343 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2344 except:
2345 print("no HDD with path %s registered" % (loc))
2346 return 0
2347
2348 progress = hdd.deleteStorage()
2349 progressBar(ctx, progress)
2350
2351 return 0
2352
2353def registerIsoCmd(ctx, args):
2354 if len(args) < 2:
2355 print("usage: registerIso location")
2356 return 0
2357
2358 vbox = ctx['vb']
2359 loc = args[1]
2360 iso = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2361 print("registered ISO as %s" % (iso.id))
2362 return 0
2363
2364def unregisterIsoCmd(ctx, args):
2365 if len(args) != 2:
2366 print("usage: unregisterIso path")
2367 return 0
2368
2369 vbox = ctx['vb']
2370 loc = args[1]
2371 try:
2372 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2373 except:
2374 print("no DVD with path %s registered" % (loc))
2375 return 0
2376
2377 progress = dvd.close()
2378 print("Unregistered ISO at %s" % (colPath(ctx, loc)))
2379
2380 return 0
2381
2382def removeIsoCmd(ctx, args):
2383 if len(args) != 2:
2384 print("usage: removeIso path")
2385 return 0
2386
2387 vbox = ctx['vb']
2388 loc = args[1]
2389 try:
2390 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2391 except:
2392 print("no DVD with path %s registered" % (loc))
2393 return 0
2394
2395 progress = dvd.deleteStorage()
2396 if progressBar(ctx, progress):
2397 print("Removed ISO at %s" % (colPath(ctx, dvd.location)))
2398 else:
2399 reportError(ctx, progress)
2400 return 0
2401
2402def attachIsoCmd(ctx, args):
2403 if len(args) < 3:
2404 print("usage: attachIso vm iso controller port:slot")
2405 return 0
2406
2407 mach = argsToMach(ctx, args)
2408 if mach is None:
2409 return 0
2410 vbox = ctx['vb']
2411 loc = args[2]
2412 try:
2413 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2414 except:
2415 print("no DVD with path %s registered" % (loc))
2416 return 0
2417 if len(args) > 3:
2418 ctr = args[3]
2419 (port, slot) = args[4].split(":")
2420 else:
2421 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD)
2422 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.attachDevice(ctr, port, slot, ctx['global'].constants.DeviceType_DVD, dvd))
2423 return 0
2424
2425def detachIsoCmd(ctx, args):
2426 if len(args) < 3:
2427 print("usage: detachIso vm iso")
2428 return 0
2429
2430 mach = argsToMach(ctx, args)
2431 if mach is None:
2432 return 0
2433 vbox = ctx['vb']
2434 loc = args[2]
2435 try:
2436 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2437 except:
2438 print("no DVD with path %s registered" % (loc))
2439 return 0
2440
2441 detachMedium(ctx, mach.id, dvd)
2442 return 0
2443
2444def mountIsoCmd(ctx, args):
2445 if len(args) < 3:
2446 print("usage: mountIso vm iso controller port:slot")
2447 return 0
2448
2449 mach = argsToMach(ctx, args)
2450 if mach is None:
2451 return 0
2452 vbox = ctx['vb']
2453 loc = args[2]
2454 try:
2455 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2456 except:
2457 print("no DVD with path %s registered" % (loc))
2458 return 0
2459
2460 if len(args) > 3:
2461 ctr = args[3]
2462 (port, slot) = args[4].split(":")
2463 else:
2464 # autodetect controller and location, just find first controller with media == DVD
2465 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD)
2466
2467 cmdExistingVm(ctx, mach, 'mountiso', [ctr, port, slot, dvd, True])
2468
2469 return 0
2470
2471def unmountIsoCmd(ctx, args):
2472 if len(args) < 2:
2473 print("usage: unmountIso vm controller port:slot")
2474 return 0
2475
2476 mach = argsToMach(ctx, args)
2477 if mach is None:
2478 return 0
2479 vbox = ctx['vb']
2480
2481 if len(args) > 3:
2482 ctr = args[2]
2483 (port, slot) = args[3].split(":")
2484 else:
2485 # autodetect controller and location, just find first controller with media == DVD
2486 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD)
2487
2488 cmdExistingVm(ctx, mach, 'mountiso', [ctr, port, slot, None, True])
2489
2490 return 0
2491
2492def attachCtr(ctx, mach, args):
2493 [name, bus, ctrltype] = args
2494 ctr = mach.addStorageController(name, bus)
2495 if ctrltype != None:
2496 ctr.controllerType = ctrltype
2497
2498def attachCtrCmd(ctx, args):
2499 if len(args) < 4:
2500 print("usage: attachCtr vm cname bus <type>")
2501 return 0
2502
2503 if len(args) > 4:
2504 ctrltype = enumFromString(ctx, 'StorageControllerType', args[4])
2505 if ctrltype == None:
2506 print("Controller type %s unknown" % (args[4]))
2507 return 0
2508 else:
2509 ctrltype = None
2510
2511 mach = argsToMach(ctx, args)
2512 if mach is None:
2513 return 0
2514 bus = enumFromString(ctx, 'StorageBus', args[3])
2515 if bus is None:
2516 print("Bus type %s unknown" % (args[3]))
2517 return 0
2518 name = args[2]
2519 cmdClosedVm(ctx, mach, attachCtr, [name, bus, ctrltype])
2520 return 0
2521
2522def detachCtrCmd(ctx, args):
2523 if len(args) < 3:
2524 print("usage: detachCtr vm name")
2525 return 0
2526
2527 mach = argsToMach(ctx, args)
2528 if mach is None:
2529 return 0
2530 ctr = args[2]
2531 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.removeStorageController(ctr))
2532 return 0
2533
2534def usbctr(ctx, mach, console, args):
2535 if args[0]:
2536 console.attachUSBDevice(args[1], "")
2537 else:
2538 console.detachUSBDevice(args[1])
2539
2540def attachUsbCmd(ctx, args):
2541 if len(args) < 3:
2542 print("usage: attachUsb vm deviceuid")
2543 return 0
2544
2545 mach = argsToMach(ctx, args)
2546 if mach is None:
2547 return 0
2548 dev = args[2]
2549 cmdExistingVm(ctx, mach, 'guestlambda', [usbctr, True, dev])
2550 return 0
2551
2552def detachUsbCmd(ctx, args):
2553 if len(args) < 3:
2554 print("usage: detachUsb vm deviceuid")
2555 return 0
2556
2557 mach = argsToMach(ctx, args)
2558 if mach is None:
2559 return 0
2560 dev = args[2]
2561 cmdExistingVm(ctx, mach, 'guestlambda', [usbctr, False, dev])
2562 return 0
2563
2564
2565def guiCmd(ctx, args):
2566 if len(args) > 1:
2567 print("usage: gui")
2568 return 0
2569
2570 binDir = ctx['global'].getBinDir()
2571
2572 vbox = os.path.join(binDir, 'VirtualBox')
2573 try:
2574 os.system(vbox)
2575 except KeyboardInterrupt:
2576 # to allow interruption
2577 pass
2578 return 0
2579
2580def shareFolderCmd(ctx, args):
2581 if len(args) < 4:
2582 print("usage: shareFolder vm path name <writable> <persistent>")
2583 return 0
2584
2585 mach = argsToMach(ctx, args)
2586 if mach is None:
2587 return 0
2588 path = args[2]
2589 name = args[3]
2590 writable = False
2591 persistent = False
2592 if len(args) > 4:
2593 for a in args[4:]:
2594 if a == 'writable':
2595 writable = True
2596 if a == 'persistent':
2597 persistent = True
2598 if persistent:
2599 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.createSharedFolder(name, path, writable), [])
2600 else:
2601 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: console.createSharedFolder(name, path, writable)])
2602 return 0
2603
2604def unshareFolderCmd(ctx, args):
2605 if len(args) < 3:
2606 print("usage: unshareFolder vm name")
2607 return 0
2608
2609 mach = argsToMach(ctx, args)
2610 if mach is None:
2611 return 0
2612 name = args[2]
2613 found = False
2614 for sf in ctx['global'].getArray(mach, 'sharedFolders'):
2615 if sf.name == name:
2616 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.removeSharedFolder(name), [])
2617 found = True
2618 break
2619 if not found:
2620 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: console.removeSharedFolder(name)])
2621 return 0
2622
2623
2624def snapshotCmd(ctx, args):
2625 if (len(args) < 2 or args[1] == 'help'):
2626 print("Take snapshot: snapshot vm take name <description>")
2627 print("Restore snapshot: snapshot vm restore name")
2628 print("Merge snapshot: snapshot vm merge name")
2629 return 0
2630
2631 mach = argsToMach(ctx, args)
2632 if mach is None:
2633 return 0
2634 cmd = args[2]
2635 if cmd == 'take':
2636 if len(args) < 4:
2637 print("usage: snapshot vm take name <description>")
2638 return 0
2639 name = args[3]
2640 if len(args) > 4:
2641 desc = args[4]
2642 else:
2643 desc = ""
2644 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.takeSnapshot(name, desc, True)[0]))
2645 return 0
2646
2647 if cmd == 'restore':
2648 if len(args) < 4:
2649 print("usage: snapshot vm restore name")
2650 return 0
2651 name = args[3]
2652 snap = mach.findSnapshot(name)
2653 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.restoreSnapshot(snap)))
2654 return 0
2655
2656 if cmd == 'restorecurrent':
2657 if len(args) < 4:
2658 print("usage: snapshot vm restorecurrent")
2659 return 0
2660 snap = mach.currentSnapshot()
2661 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.restoreSnapshot(snap)))
2662 return 0
2663
2664 if cmd == 'delete':
2665 if len(args) < 4:
2666 print("usage: snapshot vm delete name")
2667 return 0
2668 name = args[3]
2669 snap = mach.findSnapshot(name)
2670 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.deleteSnapshot(snap.id)))
2671 return 0
2672
2673 print("Command '%s' is unknown" % (cmd))
2674 return 0
2675
2676def natAlias(ctx, mach, nicnum, nat, args=[]):
2677 """This command shows/alters NAT's alias settings.
2678 usage: nat <vm> <nicnum> alias [default|[log] [proxyonly] [sameports]]
2679 default - set settings to default values
2680 log - switch on alias logging
2681 proxyonly - switch proxyonly mode on
2682 sameports - enforces NAT using the same ports
2683 """
2684 alias = {
2685 'log': 0x1,
2686 'proxyonly': 0x2,
2687 'sameports': 0x4
2688 }
2689 if len(args) == 1:
2690 first = 0
2691 msg = ''
2692 for aliasmode, aliaskey in list(alias.items()):
2693 if first == 0:
2694 first = 1
2695 else:
2696 msg += ', '
2697 if int(nat.aliasMode) & aliaskey:
2698 msg += '%s: %s' % (aliasmode, 'on')
2699 else:
2700 msg += '%s: %s' % (aliasmode, 'off')
2701 return (0, [msg])
2702 else:
2703 nat.aliasMode = 0
2704 if 'default' not in args:
2705 for a in range(1, len(args)):
2706 if args[a] not in alias:
2707 print('Invalid alias mode: ' + args[a])
2708 print(natAlias.__doc__)
2709 return (1, None)
2710 nat.aliasMode = int(nat.aliasMode) | alias[args[a]]
2711 return (0, None)
2712
2713def natSettings(ctx, mach, nicnum, nat, args):
2714 """This command shows/alters NAT settings.
2715 usage: nat <vm> <nicnum> settings [<mtu> [[<socsndbuf> <sockrcvbuf> [<tcpsndwnd> <tcprcvwnd>]]]]
2716 mtu - set mtu <= 16000
2717 socksndbuf/sockrcvbuf - sets amount of kb for socket sending/receiving buffer
2718 tcpsndwnd/tcprcvwnd - sets size of initial tcp sending/receiving window
2719 """
2720 if len(args) == 1:
2721 (mtu, socksndbuf, sockrcvbuf, tcpsndwnd, tcprcvwnd) = nat.getNetworkSettings()
2722 if mtu == 0: mtu = 1500
2723 if socksndbuf == 0: socksndbuf = 64
2724 if sockrcvbuf == 0: sockrcvbuf = 64
2725 if tcpsndwnd == 0: tcpsndwnd = 64
2726 if tcprcvwnd == 0: tcprcvwnd = 64
2727 msg = 'mtu:%s socket(snd:%s, rcv:%s) tcpwnd(snd:%s, rcv:%s)' % (mtu, socksndbuf, sockrcvbuf, tcpsndwnd, tcprcvwnd)
2728 return (0, [msg])
2729 else:
2730 if args[1] < 16000:
2731 print('invalid mtu value (%s not in range [65 - 16000])' % (args[1]))
2732 return (1, None)
2733 for i in range(2, len(args)):
2734 if not args[i].isdigit() or int(args[i]) < 8 or int(args[i]) > 1024:
2735 print('invalid %s parameter (%i not in range [8-1024])' % (i, args[i]))
2736 return (1, None)
2737 a = [args[1]]
2738 if len(args) < 6:
2739 for i in range(2, len(args)): a.append(args[i])
2740 for i in range(len(args), 6): a.append(0)
2741 else:
2742 for i in range(2, len(args)): a.append(args[i])
2743 #print(a)
2744 nat.setNetworkSettings(int(a[0]), int(a[1]), int(a[2]), int(a[3]), int(a[4]))
2745 return (0, None)
2746
2747def natDns(ctx, mach, nicnum, nat, args):
2748 """This command shows/alters DNS's NAT settings
2749 usage: nat <vm> <nicnum> dns [passdomain] [proxy] [usehostresolver]
2750 passdomain - enforces builtin DHCP server to pass domain
2751 proxy - switch on builtin NAT DNS proxying mechanism
2752 usehostresolver - proxies all DNS requests to Host Resolver interface
2753 """
2754 yesno = {0: 'off', 1: 'on'}
2755 if len(args) == 1:
2756 msg = 'passdomain:%s, proxy:%s, usehostresolver:%s' % (yesno[int(nat.DNSPassDomain)], yesno[int(nat.DNSProxy)], yesno[int(nat.DNSUseHostResolver)])
2757 return (0, [msg])
2758 else:
2759 nat.DNSPassDomain = 'passdomain' in args
2760 nat.DNSProxy = 'proxy' in args
2761 nat.DNSUseHostResolver = 'usehostresolver' in args
2762 return (0, None)
2763
2764def natTftp(ctx, mach, nicnum, nat, args):
2765 """This command shows/alters TFTP settings
2766 usage nat <vm> <nicnum> tftp [prefix <prefix>| bootfile <bootfile>| server <server>]
2767 prefix - alters prefix TFTP settings
2768 bootfile - alters bootfile TFTP settings
2769 server - sets booting server
2770 """
2771 if len(args) == 1:
2772 server = nat.TFTPNextServer
2773 if server is None:
2774 server = nat.network
2775 if server is None:
2776 server = '10.0.%d/24' % (int(nicnum) + 2)
2777 (server, mask) = server.split('/')
2778 while server.count('.') != 3:
2779 server += '.0'
2780 (a, b, c, d) = server.split('.')
2781 server = '%d.%d.%d.4' % (a, b, c)
2782 prefix = nat.TFTPPrefix
2783 if prefix is None:
2784 prefix = '%s/TFTP/' % (ctx['vb'].homeFolder)
2785 bootfile = nat.TFTPBootFile
2786 if bootfile is None:
2787 bootfile = '%s.pxe' % (mach.name)
2788 msg = 'server:%s, prefix:%s, bootfile:%s' % (server, prefix, bootfile)
2789 return (0, [msg])
2790 else:
2791
2792 cmd = args[1]
2793 if len(args) != 3:
2794 print('invalid args:', args)
2795 print(natTftp.__doc__)
2796 return (1, None)
2797 if cmd == 'prefix': nat.TFTPPrefix = args[2]
2798 elif cmd == 'bootfile': nat.TFTPBootFile = args[2]
2799 elif cmd == 'server': nat.TFTPNextServer = args[2]
2800 else:
2801 print("invalid cmd:", cmd)
2802 return (1, None)
2803 return (0, None)
2804
2805def natPortForwarding(ctx, mach, nicnum, nat, args):
2806 """This command shows/manages port-forwarding settings
2807 usage:
2808 nat <vm> <nicnum> <pf> [ simple tcp|udp <hostport> <guestport>]
2809 |[no_name tcp|udp <hostip> <hostport> <guestip> <guestport>]
2810 |[ex tcp|udp <pf-name> <hostip> <hostport> <guestip> <guestport>]
2811 |[delete <pf-name>]
2812 """
2813 if len(args) == 1:
2814 # note: keys/values are swapped in defining part of the function
2815 proto = {0: 'udp', 1: 'tcp'}
2816 msg = []
2817 pfs = ctx['global'].getArray(nat, 'redirects')
2818 for pf in pfs:
2819 (pfnme, pfp, pfhip, pfhp, pfgip, pfgp) = str(pf).split(', ')
2820 msg.append('%s: %s %s:%s => %s:%s' % (pfnme, proto[int(pfp)], pfhip, pfhp, pfgip, pfgp))
2821 return (0, msg) # msg is array
2822 else:
2823 proto = {'udp': 0, 'tcp': 1}
2824 pfcmd = {
2825 'simple': {
2826 'validate': lambda: args[1] in list(pfcmd.keys()) and args[2] in list(proto.keys()) and len(args) == 5,
2827 'func':lambda: nat.addRedirect('', proto[args[2]], '', int(args[3]), '', int(args[4]))
2828 },
2829 'no_name': {
2830 'validate': lambda: args[1] in list(pfcmd.keys()) and args[2] in list(proto.keys()) and len(args) == 7,
2831 'func': lambda: nat.addRedirect('', proto[args[2]], args[3], int(args[4]), args[5], int(args[6]))
2832 },
2833 'ex': {
2834 'validate': lambda: args[1] in list(pfcmd.keys()) and args[2] in list(proto.keys()) and len(args) == 8,
2835 'func': lambda: nat.addRedirect(args[3], proto[args[2]], args[4], int(args[5]), args[6], int(args[7]))
2836 },
2837 'delete': {
2838 'validate': lambda: len(args) == 3,
2839 'func': lambda: nat.removeRedirect(args[2])
2840 }
2841 }
2842
2843 if not pfcmd[args[1]]['validate']():
2844 print('invalid port-forwarding or args of sub command ', args[1])
2845 print(natPortForwarding.__doc__)
2846 return (1, None)
2847
2848 a = pfcmd[args[1]]['func']()
2849 return (0, None)
2850
2851def natNetwork(ctx, mach, nicnum, nat, args):
2852 """This command shows/alters NAT network settings
2853 usage: nat <vm> <nicnum> network [<network>]
2854 """
2855 if len(args) == 1:
2856 if nat.network is not None and len(str(nat.network)) != 0:
2857 msg = '\'%s\'' % (nat.network)
2858 else:
2859 msg = '10.0.%d.0/24' % (int(nicnum) + 2)
2860 return (0, [msg])
2861 else:
2862 (addr, mask) = args[1].split('/')
2863 if addr.count('.') > 3 or int(mask) < 0 or int(mask) > 32:
2864 print('Invalid arguments')
2865 return (1, None)
2866 nat.network = args[1]
2867 return (0, None)
2868
2869def natCmd(ctx, args):
2870 """This command is entry point to NAT settins management
2871 usage: nat <vm> <nicnum> <cmd> <cmd-args>
2872 cmd - [alias|settings|tftp|dns|pf|network]
2873 for more information about commands:
2874 nat help <cmd>
2875 """
2876
2877 natcommands = {
2878 'alias' : natAlias,
2879 'settings' : natSettings,
2880 'tftp': natTftp,
2881 'dns': natDns,
2882 'pf': natPortForwarding,
2883 'network': natNetwork
2884 }
2885
2886 if len(args) < 2 or args[1] == 'help':
2887 if len(args) > 2:
2888 print(natcommands[args[2]].__doc__)
2889 else:
2890 print(natCmd.__doc__)
2891 return 0
2892 if len(args) == 1 or len(args) < 4 or args[3] not in natcommands:
2893 print(natCmd.__doc__)
2894 return 0
2895 mach = ctx['argsToMach'](args)
2896 if mach == None:
2897 print("please specify vm")
2898 return 0
2899 if len(args) < 3 or not args[2].isdigit() or int(args[2]) not in list(range(0, ctx['vb'].systemProperties.getMaxNetworkAdapters(mach.chipsetType))):
2900 print('please specify adapter num %d isn\'t in range [0-%d]' % (args[2], ctx['vb'].systemProperties.getMaxNetworkAdapters(mach.chipsetType)))
2901 return 0
2902 nicnum = int(args[2])
2903 cmdargs = []
2904 for i in range(3, len(args)):
2905 cmdargs.append(args[i])
2906
2907 # @todo vvl if nicnum is missed but command is entered
2908 # use NAT func for every adapter on machine.
2909 func = args[3]
2910 rosession = 1
2911 session = None
2912 if len(cmdargs) > 1:
2913 rosession = 0
2914 session = ctx['global'].openMachineSession(mach, fPermitSharing=False)
2915 mach = session.machine
2916
2917 adapter = mach.getNetworkAdapter(nicnum)
2918 natEngine = adapter.NATEngine
2919 (rc, report) = natcommands[func](ctx, mach, nicnum, natEngine, cmdargs)
2920 if rosession == 0:
2921 if rc == 0:
2922 mach.saveSettings()
2923 session.unlockMachine()
2924 elif report is not None:
2925 for r in report:
2926 msg ='%s nic%d %s: %s' % (mach.name, nicnum, func, r)
2927 print(msg)
2928 return 0
2929
2930def nicSwitchOnOff(adapter, attr, args):
2931 if len(args) == 1:
2932 yesno = {0: 'off', 1: 'on'}
2933 r = yesno[int(adapter.__getattr__(attr))]
2934 return (0, r)
2935 else:
2936 yesno = {'off' : 0, 'on' : 1}
2937 if args[1] not in yesno:
2938 print('%s isn\'t acceptable, please choose %s' % (args[1], list(yesno.keys())))
2939 return (1, None)
2940 adapter.__setattr__(attr, yesno[args[1]])
2941 return (0, None)
2942
2943def nicTraceSubCmd(ctx, vm, nicnum, adapter, args):
2944 '''
2945 usage: nic <vm> <nicnum> trace [on|off [file]]
2946 '''
2947 (rc, r) = nicSwitchOnOff(adapter, 'traceEnabled', args)
2948 if len(args) == 1 and rc == 0:
2949 r = '%s file:%s' % (r, adapter.traceFile)
2950 return (0, r)
2951 elif len(args) == 3 and rc == 0:
2952 adapter.traceFile = args[2]
2953 return (0, None)
2954
2955def nicLineSpeedSubCmd(ctx, vm, nicnum, adapter, args):
2956 if len(args) == 1:
2957 r = '%d kbps'% (adapter.lineSpeed)
2958 return (0, r)
2959 else:
2960 if not args[1].isdigit():
2961 print('%s isn\'t a number' % (args[1]))
2962 return (1, None)
2963 adapter.lineSpeed = int(args[1])
2964 return (0, None)
2965
2966def nicCableSubCmd(ctx, vm, nicnum, adapter, args):
2967 '''
2968 usage: nic <vm> <nicnum> cable [on|off]
2969 '''
2970 return nicSwitchOnOff(adapter, 'cableConnected', args)
2971
2972def nicEnableSubCmd(ctx, vm, nicnum, adapter, args):
2973 '''
2974 usage: nic <vm> <nicnum> enable [on|off]
2975 '''
2976 return nicSwitchOnOff(adapter, 'enabled', args)
2977
2978def nicTypeSubCmd(ctx, vm, nicnum, adapter, args):
2979 '''
2980 usage: nic <vm> <nicnum> type [Am79c970A|Am79c970A|I82540EM|I82545EM|I82543GC|Virtio]
2981 '''
2982 if len(args) == 1:
2983 nictypes = ctx['const'].all_values('NetworkAdapterType')
2984 for key in list(nictypes.keys()):
2985 if str(adapter.adapterType) == str(nictypes[key]):
2986 return (0, str(key))
2987 return (1, None)
2988 else:
2989 nictypes = ctx['const'].all_values('NetworkAdapterType')
2990 if args[1] not in list(nictypes.keys()):
2991 print('%s not in acceptable values (%s)' % (args[1], list(nictypes.keys())))
2992 return (1, None)
2993 adapter.adapterType = nictypes[args[1]]
2994 return (0, None)
2995
2996def nicAttachmentSubCmd(ctx, vm, nicnum, adapter, args):
2997 '''
2998 usage: nic <vm> <nicnum> attachment [Null|NAT|Bridged <interface>|Internal <name>|HostOnly <interface>
2999 '''
3000 if len(args) == 1:
3001 nicAttachmentType = {
3002 ctx['global'].constants.NetworkAttachmentType_Null: ('Null', ''),
3003 ctx['global'].constants.NetworkAttachmentType_NAT: ('NAT', ''),
3004 ctx['global'].constants.NetworkAttachmentType_Bridged: ('Bridged', adapter.bridgedInterface),
3005 ctx['global'].constants.NetworkAttachmentType_Internal: ('Internal', adapter.internalNetwork),
3006 ctx['global'].constants.NetworkAttachmentType_HostOnly: ('HostOnly', adapter.hostOnlyInterface),
3007 # @todo show details of the generic network attachment type
3008 ctx['global'].constants.NetworkAttachmentType_Generic: ('Generic', ''),
3009 }
3010 if type(adapter.attachmentType) != int:
3011 t = str(adapter.attachmentType)
3012 else:
3013 t = adapter.attachmentType
3014 (r, p) = nicAttachmentType[t]
3015 return (0, 'attachment:%s, name:%s' % (r, p))
3016 else:
3017 nicAttachmentType = {
3018 'Null': {
3019 'v': lambda: len(args) == 2,
3020 'p': lambda: 'do nothing',
3021 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Null},
3022 'NAT': {
3023 'v': lambda: len(args) == 2,
3024 'p': lambda: 'do nothing',
3025 'f': lambda: ctx['global'].constants.NetworkAttachmentType_NAT},
3026 'Bridged': {
3027 'v': lambda: len(args) == 3,
3028 'p': lambda: adapter.__setattr__('bridgedInterface', args[2]),
3029 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Bridged},
3030 'Internal': {
3031 'v': lambda: len(args) == 3,
3032 'p': lambda: adapter.__setattr__('internalNetwork', args[2]),
3033 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Internal},
3034 'HostOnly': {
3035 'v': lambda: len(args) == 2,
3036 'p': lambda: adapter.__setattr__('hostOnlyInterface', args[2]),
3037 'f': lambda: ctx['global'].constants.NetworkAttachmentType_HostOnly},
3038 # @todo implement setting the properties of a generic attachment
3039 'Generic': {
3040 'v': lambda: len(args) == 3,
3041 'p': lambda: 'do nothing',
3042 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Generic}
3043 }
3044 if args[1] not in list(nicAttachmentType.keys()):
3045 print('%s not in acceptable values (%s)' % (args[1], list(nicAttachmentType.keys())))
3046 return (1, None)
3047 if not nicAttachmentType[args[1]]['v']():
3048 print(nicAttachmentType.__doc__)
3049 return (1, None)
3050 nicAttachmentType[args[1]]['p']()
3051 adapter.attachmentType = nicAttachmentType[args[1]]['f']()
3052 return (0, None)
3053
3054def nicCmd(ctx, args):
3055 '''
3056 This command to manage network adapters
3057 usage: nic <vm> <nicnum> <cmd> <cmd-args>
3058 where cmd : attachment, trace, linespeed, cable, enable, type
3059 '''
3060 # 'command name':{'runtime': is_callable_at_runtime, 'op': function_name}
3061 niccomand = {
3062 'attachment': nicAttachmentSubCmd,
3063 'trace': nicTraceSubCmd,
3064 'linespeed': nicLineSpeedSubCmd,
3065 'cable': nicCableSubCmd,
3066 'enable': nicEnableSubCmd,
3067 'type': nicTypeSubCmd
3068 }
3069 if len(args) < 2 \
3070 or args[1] == 'help' \
3071 or (len(args) > 2 and args[3] not in niccomand):
3072 if len(args) == 3 \
3073 and args[2] in niccomand:
3074 print(niccomand[args[2]].__doc__)
3075 else:
3076 print(nicCmd.__doc__)
3077 return 0
3078
3079 vm = ctx['argsToMach'](args)
3080 if vm is None:
3081 print('please specify vm')
3082 return 0
3083
3084 if len(args) < 3 \
3085 or int(args[2]) not in list(range(0, ctx['vb'].systemProperties.getMaxNetworkAdapters(vm.chipsetType))):
3086 print('please specify adapter num %d isn\'t in range [0-%d]'% (args[2], ctx['vb'].systemProperties.getMaxNetworkAdapters(vm.chipsetType)))
3087 return 0
3088 nicnum = int(args[2])
3089 cmdargs = args[3:]
3090 func = args[3]
3091 session = None
3092 session = ctx['global'].openMachineSession(vm, fPermitSharing=True)
3093 vm = session.machine
3094 adapter = vm.getNetworkAdapter(nicnum)
3095 (rc, report) = niccomand[func](ctx, vm, nicnum, adapter, cmdargs)
3096 if rc == 0:
3097 vm.saveSettings()
3098 if report is not None:
3099 print('%s nic %d %s: %s' % (vm.name, nicnum, args[3], report))
3100 session.unlockMachine()
3101 return 0
3102
3103
3104def promptCmd(ctx, args):
3105 if len(args) < 2:
3106 print("Current prompt: '%s'" % (ctx['prompt']))
3107 return 0
3108
3109 ctx['prompt'] = args[1]
3110 return 0
3111
3112def foreachCmd(ctx, args):
3113 if len(args) < 3:
3114 print("usage: foreach scope command, where scope is XPath-like expression //vms/vm[@CPUCount='2']")
3115 return 0
3116
3117 scope = args[1]
3118 cmd = args[2]
3119 elems = eval_xpath(ctx, scope)
3120 try:
3121 for e in elems:
3122 e.apply(cmd)
3123 except:
3124 print("Error executing")
3125 traceback.print_exc()
3126 return 0
3127
3128def foreachvmCmd(ctx, args):
3129 if len(args) < 2:
3130 print("foreachvm command <args>")
3131 return 0
3132 cmdargs = args[1:]
3133 cmdargs.insert(1, '')
3134 for mach in getMachines(ctx):
3135 cmdargs[1] = mach.id
3136 runCommandArgs(ctx, cmdargs)
3137 return 0
3138
3139def recordDemoCmd(ctx, args):
3140 if len(args) < 3:
3141 print("usage: recordDemo vm filename (duration)")
3142 return 0
3143 mach = argsToMach(ctx, args)
3144 if mach == None:
3145 return 0
3146 filename = args[2]
3147 dur = 10000
3148 if len(args) > 3:
3149 dur = float(args[3])
3150 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: recordDemo(ctx, console, filename, dur)])
3151 return 0
3152
3153def playbackDemoCmd(ctx, args):
3154 if len(args) < 3:
3155 print("usage: playbackDemo vm filename (duration)")
3156 return 0
3157 mach = argsToMach(ctx, args)
3158 if mach == None:
3159 return 0
3160 filename = args[2]
3161 dur = 10000
3162 if len(args) > 3:
3163 dur = float(args[3])
3164 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: playbackDemo(ctx, console, filename, dur)])
3165 return 0
3166
3167
3168def pciAddr(ctx, addr):
3169 strg = "%02x:%02x.%d" % (addr >> 8, (addr & 0xff) >> 3, addr & 7)
3170 return colPci(ctx, strg)
3171
3172def lspci(ctx, console):
3173 assigned = ctx['global'].getArray(console.machine, 'PCIDeviceAssignments')
3174 for a in assigned:
3175 if a.isPhysicalDevice:
3176 print("%s: assigned host device %s guest %s" % (colDev(ctx, a.name), pciAddr(ctx, a.hostAddress), pciAddr(ctx, a.guestAddress)))
3177
3178 atts = ctx['global'].getArray(console, 'attachedPCIDevices')
3179 for a in atts:
3180 if a.isPhysicalDevice:
3181 print("%s: physical, guest %s, host %s" % (colDev(ctx, a.name), pciAddr(ctx, a.guestAddress), pciAddr(ctx, a.hostAddress)))
3182 else:
3183 print("%s: virtual, guest %s" % (colDev(ctx, a.name), pciAddr(ctx, a.guestAddress)))
3184 return
3185
3186def parsePci(strg):
3187 pcire = re.compile(r'(?P<b>[0-9a-fA-F]+):(?P<d>[0-9a-fA-F]+)\.(?P<f>\d)')
3188 match = pcire.search(strg)
3189 if match is None:
3190 return -1
3191 pdict = match.groupdict()
3192 return ((int(pdict['b'], 16)) << 8) | ((int(pdict['d'], 16)) << 3) | int(pdict['f'])
3193
3194def lspciCmd(ctx, args):
3195 if len(args) < 2:
3196 print("usage: lspci vm")
3197 return 0
3198 mach = argsToMach(ctx, args)
3199 if mach == None:
3200 return 0
3201 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: lspci(ctx, console)])
3202 return 0
3203
3204def attachpciCmd(ctx, args):
3205 if len(args) < 3:
3206 print("usage: attachpci vm hostpci <guestpci>")
3207 return 0
3208 mach = argsToMach(ctx, args)
3209 if mach == None:
3210 return 0
3211 hostaddr = parsePci(args[2])
3212 if hostaddr == -1:
3213 print("invalid host PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[2]))
3214 return 0
3215
3216 if len(args) > 3:
3217 guestaddr = parsePci(args[3])
3218 if guestaddr == -1:
3219 print("invalid guest PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[3]))
3220 return 0
3221 else:
3222 guestaddr = hostaddr
3223 cmdClosedVm(ctx, mach, lambda ctx, mach, a: mach.attachHostPCIDevice(hostaddr, guestaddr, True))
3224 return 0
3225
3226def detachpciCmd(ctx, args):
3227 if len(args) < 3:
3228 print("usage: detachpci vm hostpci")
3229 return 0
3230 mach = argsToMach(ctx, args)
3231 if mach == None:
3232 return 0
3233 hostaddr = parsePci(args[2])
3234 if hostaddr == -1:
3235 print("invalid host PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[2]))
3236 return 0
3237
3238 cmdClosedVm(ctx, mach, lambda ctx, mach, a: mach.detachHostPCIDevice(hostaddr))
3239 return 0
3240
3241def gotoCmd(ctx, args):
3242 if len(args) < 2:
3243 print("usage: goto line")
3244 return 0
3245
3246 line = int(args[1])
3247
3248 ctx['scriptLine'] = line
3249
3250 return 0
3251
3252aliases = {'s':'start',
3253 'i':'info',
3254 'l':'list',
3255 'h':'help',
3256 'a':'alias',
3257 'q':'quit', 'exit':'quit',
3258 'tg': 'typeGuest',
3259 'v':'verbose'}
3260
3261commands = {'help':['Prints help information', helpCmd, 0],
3262 'start':['Start virtual machine by name or uuid: start Linux headless', startCmd, 0],
3263 'createVm':['Create virtual machine: createVm macvm MacOS', createVmCmd, 0],
3264 'removeVm':['Remove virtual machine', removeVmCmd, 0],
3265 'pause':['Pause virtual machine', pauseCmd, 0],
3266 'resume':['Resume virtual machine', resumeCmd, 0],
3267 'save':['Save execution state of virtual machine', saveCmd, 0],
3268 'stats':['Stats for virtual machine', statsCmd, 0],
3269 'powerdown':['Power down virtual machine', powerdownCmd, 0],
3270 'powerbutton':['Effectively press power button', powerbuttonCmd, 0],
3271 'list':['Shows known virtual machines', listCmd, 0],
3272 'info':['Shows info on machine', infoCmd, 0],
3273 'ginfo':['Shows info on guest', ginfoCmd, 0],
3274 'gexec':['Executes program in the guest', gexecCmd, 0],
3275 'gcopy':['Copy file to the guest', gcopyCmd, 0],
3276 'gpipe':['Pipe between host and guest', gpipeCmd, 0],
3277 'alias':['Control aliases', aliasCmd, 0],
3278 'verbose':['Toggle verbosity', verboseCmd, 0],
3279 'setvar':['Set VMs variable: setvar Fedora BIOSSettings.ACPIEnabled True', setvarCmd, 0],
3280 'eval':['Evaluate arbitrary Python construction: eval \'for m in getMachines(ctx): print(m.name, "has", m.memorySize, "M")\'', evalCmd, 0],
3281 'quit':['Exits', quitCmd, 0],
3282 'host':['Show host information', hostCmd, 0],
3283 'guest':['Execute command for guest: guest Win32 \'console.mouse.putMouseEvent(20, 20, 0, 0, 0)\'', guestCmd, 0],
3284 'monitorGuest':['Monitor what happens with the guest for some time: monitorGuest Win32 10', monitorGuestCmd, 0],
3285 'monitorGuestKbd':['Monitor guest keyboard for some time: monitorGuestKbd Win32 10', monitorGuestKbdCmd, 0],
3286 'monitorGuestMouse':['Monitor guest mouse for some time: monitorGuestMouse Win32 10', monitorGuestMouseCmd, 0],
3287 'monitorGuestMultiTouch':['Monitor guest touch screen for some time: monitorGuestMultiTouch Win32 10', monitorGuestMultiTouchCmd, 0],
3288 'monitorVBox':['Monitor what happens with VirtualBox for some time: monitorVBox 10', monitorVBoxCmd, 0],
3289 'portForward':['Setup permanent port forwarding for a VM, takes adapter number host port and guest port: portForward Win32 0 8080 80', portForwardCmd, 0],
3290 'showLog':['Show log file of the VM, : showLog Win32', showLogCmd, 0],
3291 'findLog':['Show entries matching pattern in log file of the VM, : findLog Win32 PDM|CPUM', findLogCmd, 0],
3292 'findAssert':['Find assert in log file of the VM, : findAssert Win32', findAssertCmd, 0],
3293 'reloadExt':['Reload custom extensions: reloadExt', reloadExtCmd, 0],
3294 'runScript':['Run VBox script: runScript script.vbox', runScriptCmd, 0],
3295 'sleep':['Sleep for specified number of seconds: sleep 3.14159', sleepCmd, 0],
3296 'shell':['Execute external shell command: shell "ls /etc/rc*"', shellCmd, 0],
3297 'exportVm':['Export VM in OVF format: exportVm Win /tmp/win.ovf', exportVMCmd, 0],
3298 'screenshot':['Take VM screenshot to a file: screenshot Win /tmp/win.png 1024 768 0', screenshotCmd, 0],
3299 'teleport':['Teleport VM to another box (see openportal): teleport Win anotherhost:8000 <passwd> <maxDowntime>', teleportCmd, 0],
3300 'typeGuest':['Type arbitrary text in guest: typeGuest Linux "^lls\\n&UP;&BKSP;ess /etc/hosts\\nq^c" 0.7', typeGuestCmd, 0],
3301 'openportal':['Open portal for teleportation of VM from another box (see teleport): openportal Win 8000 <passwd>', openportalCmd, 0],
3302 'closeportal':['Close teleportation portal (see openportal, teleport): closeportal Win', closeportalCmd, 0],
3303 'getextra':['Get extra data, empty key lists all: getextra <vm|global> <key>', getExtraDataCmd, 0],
3304 'setextra':['Set extra data, empty value removes key: setextra <vm|global> <key> <value>', setExtraDataCmd, 0],
3305 'gueststats':['Print available guest stats (only Windows guests with additions so far): gueststats Win32', gueststatsCmd, 0],
3306 'plugcpu':['Add a CPU to a running VM: plugcpu Win 1', plugcpuCmd, 0],
3307 'unplugcpu':['Remove a CPU from a running VM (additions required, Windows cannot unplug): unplugcpu Linux 1', unplugcpuCmd, 0],
3308 'createHdd': ['Create virtual HDD: createHdd 1000 /disk.vdi ', createHddCmd, 0],
3309 'removeHdd': ['Permanently remove virtual HDD: removeHdd /disk.vdi', removeHddCmd, 0],
3310 'registerHdd': ['Register HDD image with VirtualBox instance: registerHdd /disk.vdi', registerHddCmd, 0],
3311 'unregisterHdd': ['Unregister HDD image with VirtualBox instance: unregisterHdd /disk.vdi', unregisterHddCmd, 0],
3312 'attachHdd': ['Attach HDD to the VM: attachHdd win /disk.vdi "IDE Controller" 0:1', attachHddCmd, 0],
3313 'detachHdd': ['Detach HDD from the VM: detachHdd win /disk.vdi', detachHddCmd, 0],
3314 'registerIso': ['Register CD/DVD image with VirtualBox instance: registerIso /os.iso', registerIsoCmd, 0],
3315 'unregisterIso': ['Unregister CD/DVD image with VirtualBox instance: unregisterIso /os.iso', unregisterIsoCmd, 0],
3316 'removeIso': ['Permanently remove CD/DVD image: removeIso /os.iso', removeIsoCmd, 0],
3317 'attachIso': ['Attach CD/DVD to the VM: attachIso win /os.iso "IDE Controller" 0:1', attachIsoCmd, 0],
3318 'detachIso': ['Detach CD/DVD from the VM: detachIso win /os.iso', detachIsoCmd, 0],
3319 'mountIso': ['Mount CD/DVD to the running VM: mountIso win /os.iso "IDE Controller" 0:1', mountIsoCmd, 0],
3320 'unmountIso': ['Unmount CD/DVD from running VM: unmountIso win "IDE Controller" 0:1', unmountIsoCmd, 0],
3321 'attachCtr': ['Attach storage controller to the VM: attachCtr win Ctr0 IDE ICH6', attachCtrCmd, 0],
3322 'detachCtr': ['Detach HDD from the VM: detachCtr win Ctr0', detachCtrCmd, 0],
3323 'attachUsb': ['Attach USB device to the VM (use listUsb to show available devices): attachUsb win uuid', attachUsbCmd, 0],
3324 'detachUsb': ['Detach USB device from the VM: detachUsb win uuid', detachUsbCmd, 0],
3325 'listMedia': ['List media known to this VBox instance', listMediaCmd, 0],
3326 'listUsb': ['List known USB devices', listUsbCmd, 0],
3327 'shareFolder': ['Make host\'s folder visible to guest: shareFolder win /share share writable', shareFolderCmd, 0],
3328 'unshareFolder': ['Remove folder sharing', unshareFolderCmd, 0],
3329 'gui': ['Start GUI frontend', guiCmd, 0],
3330 'colors':['Toggle colors', colorsCmd, 0],
3331 'snapshot':['VM snapshot manipulation, snapshot help for more info', snapshotCmd, 0],
3332 'nat':['NAT (network address translation engine) manipulation, nat help for more info', natCmd, 0],
3333 'nic' : ['Network adapter management', nicCmd, 0],
3334 'prompt' : ['Control shell prompt', promptCmd, 0],
3335 'foreachvm' : ['Perform command for each VM', foreachvmCmd, 0],
3336 'foreach' : ['Generic "for each" construction, using XPath-like notation: foreach //vms/vm[@OSTypeId=\'MacOS\'] "print(obj.name)"', foreachCmd, 0],
3337 'recordDemo':['Record demo: recordDemo Win32 file.dmo 10', recordDemoCmd, 0],
3338 'playbackDemo':['Playback demo: playbackDemo Win32 file.dmo 10', playbackDemoCmd, 0],
3339 'lspci': ['List PCI devices attached to the VM: lspci Win32', lspciCmd, 0],
3340 'attachpci': ['Attach host PCI device to the VM: attachpci Win32 01:00.0', attachpciCmd, 0],
3341 'detachpci': ['Detach host PCI device from the VM: detachpci Win32 01:00.0', detachpciCmd, 0],
3342 'goto': ['Go to line in script (script-only)', gotoCmd, 0]
3343 }
3344
3345def runCommandArgs(ctx, args):
3346 c = args[0]
3347 if aliases.get(c, None) != None:
3348 c = aliases[c]
3349 ci = commands.get(c, None)
3350 if ci == None:
3351 print("Unknown command: '%s', type 'help' for list of known commands" % (c))
3352 return 0
3353 if ctx['remote'] and ctx['vb'] is None:
3354 if c not in ['connect', 'reconnect', 'help', 'quit']:
3355 print("First connect to remote server with %s command." % (colored('connect', 'blue')))
3356 return 0
3357 return ci[1](ctx, args)
3358
3359
3360def runCommand(ctx, cmd):
3361 if not cmd: return 0
3362 args = split_no_quotes(cmd)
3363 if len(args) == 0: return 0
3364 return runCommandArgs(ctx, args)
3365
3366#
3367# To write your own custom commands to vboxshell, create
3368# file ~/.VirtualBox/shellext.py with content like
3369#
3370# def runTestCmd(ctx, args):
3371# print("Testy test", ctx['vb'])
3372# return 0
3373#
3374# commands = {
3375# 'test': ['Test help', runTestCmd]
3376# }
3377# and issue reloadExt shell command.
3378# This file also will be read automatically on startup or 'reloadExt'.
3379#
3380# Also one can put shell extensions into ~/.VirtualBox/shexts and
3381# they will also be picked up, so this way one can exchange
3382# shell extensions easily.
3383def addExtsFromFile(ctx, cmds, filename):
3384 if not os.path.isfile(filename):
3385 return
3386 d = {}
3387 try:
3388 exec(compile(open(filename).read(), filename, 'exec'), d, d)
3389 for (k, v) in list(d['commands'].items()):
3390 if g_fVerbose:
3391 print("customize: adding \"%s\" - %s" % (k, v[0]))
3392 cmds[k] = [v[0], v[1], filename]
3393 except:
3394 print("Error loading user extensions from %s" % (filename))
3395 traceback.print_exc()
3396
3397
3398def checkUserExtensions(ctx, cmds, folder):
3399 folder = str(folder)
3400 name = os.path.join(folder, "shellext.py")
3401 addExtsFromFile(ctx, cmds, name)
3402 # also check 'exts' directory for all files
3403 shextdir = os.path.join(folder, "shexts")
3404 if not os.path.isdir(shextdir):
3405 return
3406 exts = os.listdir(shextdir)
3407 for e in exts:
3408 # not editor temporary files, please.
3409 if e.endswith('.py'):
3410 addExtsFromFile(ctx, cmds, os.path.join(shextdir, e))
3411
3412def getHomeFolder(ctx):
3413 if ctx['remote'] or ctx['vb'] is None:
3414 if 'VBOX_USER_HOME' in os.environ:
3415 return os.path.join(os.environ['VBOX_USER_HOME'])
3416 return os.path.join(os.path.expanduser("~"), ".VirtualBox")
3417 else:
3418 return ctx['vb'].homeFolder
3419
3420def interpret(ctx):
3421 if ctx['remote']:
3422 commands['connect'] = ["Connect to remote VBox instance: connect http://server:18083 user password", connectCmd, 0]
3423 commands['disconnect'] = ["Disconnect from remote VBox instance", disconnectCmd, 0]
3424 commands['reconnect'] = ["Reconnect to remote VBox instance", reconnectCmd, 0]
3425 ctx['wsinfo'] = ["http://localhost:18083", "", ""]
3426
3427 vbox = ctx['vb']
3428 if vbox is not None:
3429 try:
3430 print("Running VirtualBox version %s" % (vbox.version))
3431 except Exception as e:
3432 printErr(ctx, e)
3433 if g_fVerbose:
3434 traceback.print_exc()
3435 ctx['perf'] = None # ctx['global'].getPerfCollector(vbox)
3436 else:
3437 ctx['perf'] = None
3438
3439 home = getHomeFolder(ctx)
3440 checkUserExtensions(ctx, commands, home)
3441 if platform.system() in ['Windows', 'Microsoft']:
3442 global g_fHasColors
3443 g_fHasColors = False
3444 hist_file = os.path.join(home, ".vboxshellhistory")
3445 autoCompletion(commands, ctx)
3446
3447 if g_fHasReadline and os.path.exists(hist_file):
3448 readline.read_history_file(hist_file)
3449
3450 # to allow to print actual host information, we collect info for
3451 # last 150 secs maximum, (sample every 10 secs and keep up to 15 samples)
3452 if ctx['perf']:
3453 try:
3454 ctx['perf'].setup(['*'], [vbox.host], 10, 15)
3455 except:
3456 pass
3457 cmds = []
3458
3459 if g_sCmd is not None:
3460 cmds = g_sCmd.split(';')
3461 it = cmds.__iter__()
3462
3463 while True:
3464 try:
3465 if g_fBatchMode:
3466 cmd = 'runScript %s'% (g_sScriptFile)
3467 elif g_sCmd is not None:
3468 cmd = next(it)
3469 else:
3470 if sys.version_info[0] <= 2:
3471 cmd = raw_input(ctx['prompt'])
3472 else:
3473 cmd = input(ctx['prompt'])
3474 done = runCommand(ctx, cmd)
3475 if done != 0: break
3476 if g_fBatchMode:
3477 break
3478 except KeyboardInterrupt:
3479 print('====== You can type quit or q to leave')
3480 except StopIteration:
3481 break
3482 except EOFError:
3483 break
3484 except Exception as e:
3485 printErr(ctx, e)
3486 if g_fVerbose:
3487 traceback.print_exc()
3488 ctx['global'].waitForEvents(0)
3489 try:
3490 # There is no need to disable metric collection. This is just an example.
3491 if ct['perf']:
3492 ctx['perf'].disable(['*'], [vbox.host])
3493 except:
3494 pass
3495 if g_fHasReadline:
3496 readline.write_history_file(hist_file)
3497
3498def runCommandCb(ctx, cmd, args):
3499 args.insert(0, cmd)
3500 return runCommandArgs(ctx, args)
3501
3502def runGuestCommandCb(ctx, uuid, guestLambda, args):
3503 mach = machById(ctx, uuid)
3504 if mach == None:
3505 return 0
3506 args.insert(0, guestLambda)
3507 cmdExistingVm(ctx, mach, 'guestlambda', args)
3508 return 0
3509
3510def main(argv):
3511
3512 #
3513 # Parse command line arguments.
3514 #
3515 parse = OptionParser()
3516 parse.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False, help = "switch on verbose")
3517 parse.add_option("-a", "--autopath", dest="autopath", action="store_true", default=False, help = "switch on autopath")
3518 parse.add_option("-w", "--webservice", dest="style", action="store_const", const="WEBSERVICE", help = "connect to webservice")
3519 parse.add_option("-b", "--batch", dest="batch_file", help = "script file to execute")
3520 parse.add_option("-c", dest="command_line", help = "command sequence to execute")
3521 parse.add_option("-o", dest="opt_line", help = "option line")
3522 global g_fVerbose, g_sScriptFile, g_fBatchMode, g_fHasColors, g_fHasReadline, g_sCmd
3523 (options, args) = parse.parse_args()
3524 g_fVerbose = options.verbose
3525 style = options.style
3526 if options.batch_file is not None:
3527 g_fBatchMode = True
3528 g_fHasColors = False
3529 g_fHasReadline = False
3530 g_sScriptFile = options.batch_file
3531 if options.command_line is not None:
3532 g_fHasColors = False
3533 g_fHasReadline = False
3534 g_sCmd = options.command_line
3535
3536 params = None
3537 if options.opt_line is not None:
3538 params = {}
3539 strparams = options.opt_line
3540 strparamlist = strparams.split(',')
3541 for strparam in strparamlist:
3542 (key, value) = strparam.split('=')
3543 params[key] = value
3544
3545 if options.autopath:
3546 asLocations = [ os.getcwd(), ]
3547 try: sScriptDir = os.path.dirname(os.path.abspath(__file__))
3548 except: pass # In case __file__ isn't there.
3549 else:
3550 if platform.system() in [ 'SunOS', ]:
3551 asLocations.append(os.path.join(sScriptDir, 'amd64'))
3552 asLocations.append(sScriptDir)
3553
3554
3555 sPath = os.environ.get("VBOX_PROGRAM_PATH")
3556 if sPath is None:
3557 for sCurLoc in asLocations:
3558 if os.path.isfile(os.path.join(sCurLoc, "VirtualBox")) \
3559 or os.path.isfile(os.path.join(sCurLoc, "VirtualBox.exe")):
3560 print("Autodetected VBOX_PROGRAM_PATH as", sCurLoc)
3561 os.environ["VBOX_PROGRAM_PATH"] = sCurLoc
3562 sPath = sCurLoc
3563 break
3564 if sPath:
3565 sys.path.append(os.path.join(sPath, "sdk", "installer"))
3566
3567 sPath = os.environ.get("VBOX_SDK_PATH")
3568 if sPath is None:
3569 for sCurLoc in asLocations:
3570 if os.path.isfile(os.path.join(sCurLoc, "sdk", "bindings", "VirtualBox.xidl")):
3571 sCurLoc = os.path.join(sCurLoc, "sdk")
3572 print("Autodetected VBOX_SDK_PATH as", sCurLoc)
3573 os.environ["VBOX_SDK_PATH"] = sCurLoc
3574 sPath = sCurLoc
3575 break
3576 if sPath:
3577 sCurLoc = sPath
3578 sTmp = os.path.join(sCurLoc, 'bindings', 'xpcom', 'python')
3579 if os.path.isdir(sTmp):
3580 sys.path.append(sTmp)
3581 del sTmp
3582 del sPath, asLocations
3583
3584
3585 #
3586 # Set up the shell interpreter context and start working.
3587 #
3588 from vboxapi import VirtualBoxManager
3589 oVBoxMgr = VirtualBoxManager(style, params)
3590 ctx = {
3591 'global': oVBoxMgr,
3592 'vb': oVBoxMgr.getVirtualBox(),
3593 'const': oVBoxMgr.constants,
3594 'remote': oVBoxMgr.remote,
3595 'type': oVBoxMgr.type,
3596 'run': lambda cmd, args: runCommandCb(ctx, cmd, args),
3597 'guestlambda': lambda uuid, guestLambda, args: runGuestCommandCb(ctx, uuid, guestLambda, args),
3598 'machById': lambda uuid: machById(ctx, uuid),
3599 'argsToMach': lambda args: argsToMach(ctx, args),
3600 'progressBar': lambda p: progressBar(ctx, p),
3601 'typeInGuest': typeInGuest,
3602 '_machlist': None,
3603 'prompt': g_sPrompt,
3604 'scriptLine': 0,
3605 'interrupt': False,
3606 }
3607 interpret(ctx)
3608
3609 #
3610 # Release the interfaces references in ctx before cleaning up.
3611 #
3612 for sKey in list(ctx.keys()):
3613 del ctx[sKey]
3614 ctx = None
3615 gc.collect()
3616
3617 oVBoxMgr.deinit()
3618 del oVBoxMgr
3619
3620if __name__ == '__main__':
3621 main(sys.argv)
3622
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