VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

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

© 2023 Oracle
ContactPrivacy policyTerms of Use