VirtualBox

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

Last change on this file since 95414 was 95396, checked in by vboxsync, 3 years ago

FE/Qt, vboxshell.py: Restore previous touchscreen-only GUI event behavior, and let vboxshell.py show which multi-touch event type it is. bugref:9891

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