VirtualBox

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

Last change on this file since 74942 was 72919, checked in by vboxsync, 6 years ago

Main/*: From now on any valid UTF8 string is considered a valid guest OS type. Of course not all of them are known, so the API clients must be prepared to deal with not having a matching IGuestOSType object.
Frontends/VBoxManage+VBoxShell: adjust to deal with the change

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

© 2023 Oracle
ContactPrivacy policyTerms of Use