""" Script to export/import VirtualBox virtual machines. (c) Lukas Cenovsky, 2009, cenovsky AT bakalari DOT cz Licence: BSD Required modules: vboxapi - comes with VirtualBox ConfigObj 4 - http://www.voidspace.org.uk/python/configobj.html """ import os import sys import time from shutil import copyfile from vboxapi import VirtualBoxManager from configobj import ConfigObj # items in the list need special care when creating virtual machine machine_properties = [ 'name', 'id', 'OSTypeId', ] # items in the list are automatically read/write to the virtual machine machine_attributes = [ 'description', 'HardwareVersion', 'CPUCount', 'memorySize', #'memoryBalloonSize', #'statisticsUpdateInterval', 'VRAMSize', 'accelerate3DEnabled', 'monitorCount', 'BIOSSettings.logoFadeIn', 'BIOSSettings.logoFadeOut', 'BIOSSettings.logoDisplayTime', #'BIOSSettings.logoImagePath', 'BIOSSettings.bootMenuMode', 'BIOSSettings.ACPIEnabled', 'BIOSSettings.IOAPICEnabled', 'BIOSSettings.timeOffset', 'BIOSSettings.PXEDebugEnabled', 'HWVirtExEnabled', 'HWVirtExNestedPagingEnabled', 'HWVirtExVPIDEnabled', 'PAEEnabled', 'VRDPServer.enabled', 'VRDPServer.port', #'VRDPServer.netAddress', 'VRDPServer.authType', 'VRDPServer.authTimeout', 'VRDPServer.allowMultiConnection', 'VRDPServer.reuseSingleConnection', #'DVDDrive.getImage()', #'DVDDrive.getHostDrive()', #'floppyDrive.getImage()', #'floppyDrive.getHostDrive()', 'audioAdapter.enabled', 'audioAdapter.audioController', 'audioAdapter.audioDriver', 'clipboardMode', ] # items in the list need special care when creating virtual machine network_properties = [ 'attachmentType', ] # items in the list are automatically read/write to the virtual machine network_attributes = [ 'adapterType', 'enabled', 'MACAddress', 'hostInterface', 'internalNetwork', 'NATNetwork', 'cableConnected', 'lineSpeed', 'traceEnabled', 'traceFile', ] def out(s, *p): sys.stdout.write(s % p) def get_input(message, valid): """ show @message and waits until user enters @valid input return the selected valid option entered by the user @message - message with available options @valid - list of string that are accepted as valid input """ out(message) i = raw_input() while i not in valid: out('Not valid input %r - please try again.\n' % i) out(message) i = raw_input() return i class VBoxUtil(object): """ helper class to export/import VirtualBox virtual machines """ def __init__(self): """ initialize helper class and run the main menu""" self.vbm = VirtualBoxManager(None, None) self.const = self.vbm.constants self.vbox = self.vbm.vbox self.inifile = 'wm.ini' self.export_dir = 'exp' self.main_menu() def get_machines(self): """ return a list of virtual machines return list of tuples: (order, machine name, IMachine) """ return [(i+1, machine.name, machine) for i, machine in enumerate(self.vbox.machines)] def get_snapshots(self, machine): """ return a list of snapshots for @machine return list of tuples: (order, snapshot name, ISnapshot) @machine - IMachine interface from VirtualBox """ snapshots = [(1, 'current state', None, machine)] s = machine.currentSnapshot while s: snapshots.append((len(snapshots)+1, '%s%s' % (s.name, ' (online)' if s.online else ''), s, s.machine)) s = s.parent return snapshots def export_machine(self): """ export selected machine and snapshot """ if not os.path.isdir(self.export_dir): os.mkdir(self.export_dir) mach_dir = '%s%s' % (self.currentMachine.name, time.strftime('-%Y-%m-%d-%H-%M')) exp_dir = os.path.join(self.export_dir, mach_dir) if not os.path.isdir(exp_dir): os.mkdir(exp_dir) else: out("'%s' already exist" % exp_dir) return cfg = ConfigObj(os.path.join(exp_dir, self.inifile), unrepr=True) # machine info if self.currentSnapshot: cfg['snapshotName'] = self.currentSnapshot.name cfg['snapshotDesc'] = self.currentSnapshot.description cfg['snapshotCreated'] = time.ctime(float( self.currentSnapshot.timeStamp)/1000) else: cfg['snapshotName'] = 'current state' cfg['snapshotCreated'] = time.ctime(float( self.currentSnapshotMachine.lastStateChange)/1000) cfg['MachineSaved'] = time.strftime('%c') out("Exporting machine '%s' (%s)...", self.currentSnapshotMachine.name, cfg['snapshotName']) # machine properties and attributes for attr in machine_properties: cfg[attr] = eval('self.currentSnapshotMachine.%s' % attr) cfg['attributes'] = {} for attr in machine_attributes: cfg['attributes'][attr] = eval( 'self.currentSnapshotMachine.%s' % attr) # network 0 cfg['network0'] = {} for attr in network_properties: cfg['network0'][attr] = eval( 'self.currentSnapshotMachine.getNetworkAdapter(0).%s' % attr) cfg['network0']['attributes'] = {} for attr in network_attributes: cfg['network0']['attributes'][attr] = eval( 'self.currentSnapshotMachine.getNetworkAdapter(0).%s' % attr) # hdd IDE 0 0 cfg['hddide00'] = {} hdd_file = os.path.abspath(os.path.join(exp_dir, mach_dir+'.vdi')) cfg['hddide00']['file'] = os.path.split(hdd_file)[1] clone_hd = self.vbox.createHardDisk('', hdd_file) source_hd = self.currentSnapshotMachine.getHardDisk('IDE', 0, 0) out("\nClonning HDD '%s' to '%s'... ", source_hd.name, hdd_file) progress = source_hd.cloneTo(clone_hd, self.const.HardDiskVariant_Standard, None) while not progress.completed: progress.waitForCompletion(15000) out('%s%% ', progress.percent) clone_hd.close() #out("\nCompacting HDD '%s'... ", clone_hd.name) #progress = clone_hd.compact() #while not progress.completed: #progress.waitForCompletion(15000) #out('%s%% ', progress.percent) out('done\n') cfg.write() def export_machine_snapshot_menu(self): self.currentSnapshotMachine = None snapshots = self.get_snapshots(self.currentMachine) snapshots_lst = '\n'.join('%3d. %s' % (i, n) for i, n, s, m in snapshots) menu = ("\nPlease select a snapshot of '%s' to export:\n" '%s\n' ' q: main menu\n' '>> ' % (self.currentMachine.name, snapshots_lst)) i = get_input(menu, ['q'] + map(str, range(1, len(snapshots)+1))) if i != 'q': self.currentSnapshot = snapshots[int(i)-1][2] self.currentSnapshotMachine = snapshots[int(i)-1][3] self.export_machine() def export_machine_menu(self): self.currentMachine = None machines = self.get_machines() machines_lst = '\n'.join('%3d. %s' % (i, n) for i, n, m in machines) menu = ('\nPlease select a machine to export:\n' '%s\n' ' q: main menu\n' '>> ' % machines_lst) i = get_input(menu, ['q'] + map(str, range(1, len(machines)+1))) if i != 'q': self.currentMachine = machines[int(i)-1][2] self.export_machine_snapshot_menu() def import_machine_menu(self): machines = os.listdir(self.export_dir) machines_lst = '\n'.join('%3d. %s' % (i, n) for i, n in zip(range(1, len(machines)+1), machines)) menu = ('\nPlease select a machine to import:\n' '%s\n' ' q: main menu\n' '>> ' % machines_lst) i = get_input(menu, ['q'] + map(str, range(1, len(machines)+1))) if i != 'q': cfg = ConfigObj(os.path.join( self.export_dir, machines[int(i)-1], self.inifile), unrepr=True) out("Importing machine '%s' (%s)...\n", machines[int(i)-1], cfg['OSTypeId']) nm = self.vbox.createMachine(machines[int(i)-1], cfg['OSTypeId'], '', None) for attr, val in cfg['attributes'].iteritems(): exec 'nm.%s = %r' % (attr, val) # setting the network adapter 0 net_type = cfg['network0']['attachmentType'] if net_type == self.const.NetworkAttachmentType_NAT: nm.getNetworkAdapter(0).attachToNAT() elif net_type == self.const.NetworkAttachmentType_Bridged: nm.getNetworkAdapter(0).attachToBridgedInterface() elif net_type == self.const.NetworkAttachmentType_Internal: nm.getNetworkAdapter(0).attachToInternalNetwork() elif net_type == self.const.NetworkAttachmentType_HostOnly: nm.getNetworkAdapter(0).attachToHostOnlyInterface() else: nm.getNetworkAdapter(0).detach() for attr, val in cfg['network0']['attributes'].iteritems(): exec 'nm.getNetworkAdapter(0).%s = %r' % (attr, val) nm.saveSettings() self.vbox.registerMachine(nm) # attach hdd to the virtual machine out("Attaching HDD '%s'...\n", cfg['hddide00']['file']) dst = os.path.join( self.vbox.systemProperties.defaultHardDiskFolder, cfg['hddide00']['file']) copyfile(os.path.join( os.path.split(cfg.filename)[0], cfg['hddide00']['file']), dst) hdd = self.vbox.openHardDisk( dst, self.const.AccessMode_ReadWrite, False, '', False, '') s = self.vbm.openMachineSession(nm.id) s.machine.attachHardDisk(hdd.id, 'IDE', 0, 0) s.machine.saveSettings() self.vbm.closeMachineSession(s) out('done\n') def main_menu(self): menu = ('\nPlease select:\n' ' l: list virtual machines\n' ' e: export machine\n' ' i: import machine\n' ' q: quit\n' '>> ') i = None while i != 'q': i = get_input(menu, ('l', 'e', 'i', 'q')) if i == 'l': out('List of Virtual machines:\n') for i, n, m in self.get_machines(): out('%3d. %s\n', i, n) elif i == 'e': self.export_machine_menu() elif i == 'i': self.import_machine_menu() if __name__ == '__main__': VBoxUtil()