VirtualBox

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

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

Main: Guest Properties: fix python related build issues (related to r150440), ​​​bugref:10185.

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

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