VirtualBox

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

最後變更 在這個檔案從102872是 102872,由 vboxsync 提交於 15 月 前

Frontends/VBoxShell: First attempt to revive and unbreak this -- a lot of stuff was broken, also pylint was freaking out at certain places. More Python 3.x fixes. Made syntax help (a lot!) more consistent, although it's not perfect yet.

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

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