#!/usr/bin/env python ## Author: David Markey , Citrix Systems. ## Licence: GNU LESSER GENERAL PUBLIC LICENSE V3, http://www.gnu.org/licenses/lgpl-3.0.txt ## THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! USE AT YOUR OWN RISK! ## README before use. __version__ = "1.2.1" import os import tarfile import cStringIO import sys import copy ## Legacy Python 2.4 stuff try: import xml.etree.ElementTree as ET except: import elementtree.ElementTree as ET try: from hashlib import sha1 except: from sha import sha as sha1 try: from uuid import uuid1 as uuid except: ## RHEL/Python 2.4 is missing uuid, Dirty hack here import commands def uuid(): return commands.getoutput("uuidgen") KERNEL_PREFIX="/boot/guest" os.SEEK_SET, os.SEEK_CUR, os.SEEK_END = range(3) ## XML BLOB !## XML_DATA=" version hostname cheesy-2 date 2009-12-02 product_version 5.5.0 product_brand XenServer build_number 24648p xapi_major 1 xapi_minor 3 export_vsn 2 objects class VM id Ref:0 snapshot uuid UUID1 allowed_operations export clone copy current_operations OpaqueRef:NULL export power_state Halted name_label ~Unnamed name_description user_version 1 is_a_template 0 suspend_VDI OpaqueRef:NULL resident_on OpaqueRef:NULL affinity OpaqueRef:b8c1cff1-2b1a-04c6-cf05-e8f832a0c369 memory_target 268435456 memory_static_max 268435456 memory_dynamic_max 268435456 memory_dynamic_min 268435456 memory_static_min 16777216 VCPUs_params VCPUs_max 1 VCPUs_at_startup 1 actions_after_shutdown destroy actions_after_reboot restart actions_after_crash restart consoles VIFs Ref:1 VBDs Ref:3 Ref:6 crash_dumps VTPMs PV_bootloader PV_kernel PV_ramdisk PV_args PV_bootloader_args PV_legacy_args HVM_boot_policy BIOS order HVM_boot_params order dc HVM_shadow_multiplier 1 platform nx false acpi true apic true pae true viridian true PCI_bus other_config install-methods cdrom mac_seed domid -1 domarch last_boot_CPU_flags is_control_domain 0 metrics OpaqueRef:NULL guest_metrics OpaqueRef:NULL last_booted_record recommendations <restrictions><restriction field="memory-static-max" max="34359738368" /><restriction field="vcpus-max" max="8" /><restriction property="number-of-vbds" max="7" /><restriction property="number-of-vifs" max="7" /></restrictions> xenstore_data ha_always_run 0 ha_restart_priority is_a_snapshot 0 snapshot_of OpaqueRef:NULL snapshots snapshot_time 19700101T00:00:00Z transportable_snapshot_id blobs tags blocked_operations class VBD id Ref:6 snapshot uuid UUID2 allowed_operations attach eject current_operations VM Ref:0 VDI Ref:7 device userdevice 3 bootable 0 mode RO type CD unpluggable 1 storage_lock 0 empty 1 other_config currently_attached 0 status_code 0 status_detail runtime_properties qos_algorithm_type qos_algorithm_params qos_supported_algorithms metrics OpaqueRef:NULL class VBD id Ref:3 snapshot uuid UUID3 allowed_operations attach current_operations VM Ref:0 VDI Ref:4 device xvda userdevice 0 bootable 1 mode RW type Disk unpluggable 0 storage_lock 0 empty 0 other_config owner true currently_attached 0 status_code 0 status_detail runtime_properties qos_algorithm_type qos_algorithm_params qos_supported_algorithms metrics OpaqueRef:NULL class VIF id Ref:1 snapshot uuid UUID4 allowed_operations attach current_operations device 0 network Ref:2 VM Ref:0 MAC 00:00:00:00:00:00 MTU 0 other_config currently_attached 0 status_code 0 status_detail runtime_properties qos_algorithm_type qos_algorithm_params qos_supported_algorithms metrics OpaqueRef:NULL MAC_autogenerated 1 class network id Ref:2 snapshot uuid UUID5 name_label Pool-wide network associated with eth0 name_description allowed_operations current_operations VIFs Ref:1 PIFs other_config bridge xenbr0 blobs tags class VDI id Ref:7 snapshot uuid UUID6 name_label IDE 0.0 name_description allowed_operations current_operations SR Ref:8 VBDs Ref:6 crash_dumps virtual_size 4294965248 physical_utilisation 4294965248 type user sharable 0 read_only 1 other_config storage_lock 0 location /dev/xapi/cd/hda managed 1 missing 0 parent OpaqueRef:NULL xenstore_data sm_config hotplugged_at 2010-02-10T10:39:52Z is_a_snapshot 0 snapshot_of OpaqueRef:NULL snapshots snapshot_time 19700101T00:00:00Z tags class VDI id Ref:4 snapshot uuid UUID7 name_label 0 name_description allowed_operations clone destroy resize current_operations SR Ref:5 VBDs Ref:3 crash_dumps virtual_size 5368709120 physical_utilisation 5385486336 type system sharable 0 read_only 0 other_config storage_lock 0 location ebe16ffc-7f5d-4761-9cfe-2f052577d64d managed 1 missing 0 parent OpaqueRef:NULL xenstore_data sm_config vdi_type vhd is_a_snapshot 0 snapshot_of OpaqueRef:NULL snapshots snapshot_time 19700101T00:00:00Z tags class SR id Ref:5 snapshot uuid UUID8 name_label Local storage name_description allowed_operations forget vdi_create vdi_snapshot plug update destroy vdi_destroy scan vdi_clone vdi_resize unplug current_operations VDIs Ref:4 PBDs virtual_allocation 39313211392 physical_utilisation 39325794304 physical_size 71777124352 type lvm content_type user shared 0 other_config i18n-original-value-name_label Local storage i18n-key local-storage tags sm_config allocation thick use_vhd true devserial scsi-SATA_WDC_WD800JD-75M_WD-WMAM9AAE1521 blobs class SR id Ref:8 snapshot uuid UUID9 name_label DVD drives name_description Physical DVD drives allowed_operations forget vdi_introduce plug update destroy scan vdi_clone unplug current_operations VDIs Ref:7 PBDs virtual_allocation 4294965248 physical_utilisation 4294965248 physical_size 4294965248 type udev content_type iso shared 0 other_config i18n-original-value-name_description Physical DVD drives i18n-original-value-name_label DVD drives i18n-key local-hotplug-cd tags sm_config type cd blobs " ## Taken from http://code.activestate.com/recipes/168639-progress-bar-class/ class ProgressBar: def __init__(self, min_value = 0, max_value = 100, width=77,**kwargs): self.char = kwargs.get('char', '#') self.mode = kwargs.get('mode', 'dynamic') # fixed or dynamic if not self.mode in ['fixed', 'dynamic']: self.mode = 'fixed' self.bar = '' self.min = min_value self.max = max_value self.span = max_value - min_value self.width = width self.amount = 0 # When amount == max, we are 100% done self.update_amount(0) def increment_amount(self, add_amount = 1): """ Increment self.amount by 'add_ammount' or default to incrementing by 1, and then rebuild the bar string. """ new_amount = self.amount + add_amount if new_amount < self.min: new_amount = self.min if new_amount > self.max: new_amount = self.max self.amount = new_amount self.build_bar() def update_amount(self, new_amount = None): """ Update self.amount with 'new_amount', and then rebuild the bar string. """ if not new_amount: new_amount = self.amount if new_amount < self.min: new_amount = self.min if new_amount > self.max: new_amount = self.max self.amount = new_amount self.build_bar() def build_bar(self): """ Figure new percent complete, and rebuild the bar string base on self.amount. """ diff = float(self.amount - self.min) percent_done = int(round((diff / float(self.span)) * 100.0)) # figure the proper number of 'character' make up the bar all_full = self.width - 2 num_hashes = int(round((percent_done * all_full) / 100)) if self.mode == 'dynamic': # build a progress bar with self.char (to create a dynamic bar # where the percent string moves along with the bar progress. self.bar = self.char * num_hashes else: # build a progress bar with self.char and spaces (to create a # fixe bar (the percent string doesn't move) self.bar = self.char * num_hashes + ' ' * (all_full-num_hashes) percent_str = str(percent_done) + "%" self.bar = '[ ' + self.bar + ' ] ' + percent_str def __str__(self): return str(self.bar) class Xva(object): def __init__(self, memory="268435456", vcpus=1, name="Unnamed"): self.disks = [] self.vifs = [] self.nextref = 9 self.next_disk_letter = 1 raw_xml = XML_DATA self.dir_uuid = None self.local_kernel = None self.local_ramdisk = None self.conn = None for olduuid in [ 'UUID1', 'UUID2', 'UUID3', 'UUID4', 'UUID5', 'UUID6' , 'UUID7', 'UUID8' ,'UUID9' ]: raw_xml = raw_xml.replace(olduuid, str(uuid())) self.tree = ET.fromstring(raw_xml) xml_objects = {} config_struct = xml_objects['config_struct'] = self.tree.findall("struct/member")[1].find("value")[0][0][0][0][2][1][0] config_members = xml_objects['config_members'] = config_struct.findall("member") xml_objects['VIFS_config'] = config_members[23].find("value").findall("array") xml_objects['VBD_config'] = config_members[24].find("value").findall("array") xml_objects['PV_bootloader'] = config_members[27].find("value") xml_objects['PV_kernel'] = config_members[28].find("value") xml_objects['PV_ramdisk'] = config_members[29].find("value") xml_objects['PV_args'] = config_members[30].find("value") xml_objects['PV_bootloader_args'] = config_members[31].find("value") xml_objects['memory_static_max'] = config_members[12].find("value") xml_objects['memory_dynamic_max'] = config_members[13].find("value") xml_objects['memory_dynamic_min'] = config_members[14].find("value") xml_objects['vcpus'] = config_members[18].find("value") xml_objects['vcpus_max'] = config_members[17].find("value") xml_objects['HVM_boot_policy'] = config_members[33].find("value") xml_objects['nx'] = config_members[36][1][0][0][1].text xml_objects['acpi'] = config_members[36][1][0][1][1].text xml_objects['apic'] = config_members[36][1][0][2][1].text xml_objects['pae'] = config_members[36][1][0][3][1].text xml_objects['viridian'] = config_members[36][1][0][4][1].text xml_objects['name'] = config_members[4].find("value") xml_objects['first_vbd'] = self.tree.findall("struct/member")[1][1][0][0][2] xml_objects['first_vif'] = self.tree.findall("struct/member")[1][1][0][0][3][0] xml_objects['first_vdi'] = self.tree.findall("struct/member")[1][1][0][0][6] xml_objects['objects_array'] = self.tree.findall("struct/member")[1][1][0][0] self.xml_objects = xml_objects self.set_memory(memory) self.set_vcpus(vcpus) self.set_name(name) def new_ref(self): tmp = self.nextref self.nextref = self.nextref + 1 return "Ref:%d" % tmp def set_kernel(self, value): self.xml_objects['PV_kernel'].text = value self.xml_objects['PV_bootloader'].text = "" self.is_pv() def set_ramdisk(self, value): self.xml_objects['PV_ramdisk'].text = value def set_local_kernel(self, value): if os.path.isfile(value): self.local_kernel = value new_uuid = uuid() self.dir_uuid = new_uuid path = KERNEL_PREFIX+"/%s/vmlinuz" % new_uuid self.set_kernel(path) return True else: print "kernel %s does not exist" % value return False def set_local_ramdisk(self,value): if os.path.isfile(value): if not self.dir_uuid: print "Set a kernel first" return False self.local_ramdisk = value path = KERNEL_PREFIX+"/%s/initrd" % self.dir_uuid self.set_ramdisk(path) return True else: print "ramdisk %s does not exist" % value return False def set_args(self, value): self.xml_objects['PV_args'].text = value def append_args(self, value): if not self.xml_objects['PV_args'].text: self.xml_objects['PV_args'].text = "" self.xml_objects['PV_args'].text = self.xml_objects['PV_args'].text + " " + value def set_bootloader_args(self, value): self.xml_objects['PV_bootloader_args'].text = value def set_nx(self, value): if value: self.xml_objects['nx'] = "true" else: self.xml_objects['nx'] = "false" def set_acpi(self, value): if value: self.xml_objects['acpi'] = "true" else: self.xml_objects['acpi'] = "false" def set_apic(self, value): if value: self.xml_objects['apic'] = "true" else: self.xml_objects['apic'] = "false" def set_pae(self, value): if value: self.xml_objects['pae'] = "true" else: self.xml_objects['pae'] = "false" def set_viridian(self, value): if value: self.xml_objects['viridian'] = "true" else: self.xml_objects['viridian'] = "false" def print_report(self): print "VM Details:" print "Name: %s" % self.xml_objects['name'].text if self.xml_objects['HVM_boot_policy'].text == "BIOS order": print "Type: HVM" hvm=True else: print "Type: Paravirtualised" hvm=False print "VCPUS: %s" % self.xml_objects['vcpus'].text print "Memory(bytes): %s" % self.xml_objects['memory_static_max'].text print "ACPI: %s" % self.xml_objects['acpi'] print "APIC: %s" % self.xml_objects['apic'] print "PAE: %s" % self.xml_objects['pae'] print "NX: %s" % self.xml_objects['nx'] print "Viridian: %s" % self.xml_objects['viridian'] iteration = 0 for disk in self.disks: if iteration == 0: if hvm: print "Disk 0(Bootable): %s" % disk[1] else: print "Disk xvda(Bootable): %s" % disk[1] else: if hvm: print "Disk %d: %s" % ( iteration , disk[1]) else: print "Disk xvd%c: %s" % ( iteration + 97, disk[1]) iteration = iteration + 1 def add_disk(self, path): input_file = open(path, "rb") input_file.seek(0,os.SEEK_END) size = input_file.tell() if len(self.disks) == 0: self.xml_objects['first_vdi'][0][2][1][0][8][1].text = str(size) self.xml_objects['first_vdi'][0][2][1][0][9][1].text = str(size) ref = self.xml_objects['first_vdi'][0][1][1].text disk = (ref, path , size) self.disks.append(disk) else: ## copy a new vbd new_vbd_ref = self.new_ref() new_vdi_ref = self.new_ref() ## copy a new vbd new_vbd = copy.deepcopy(self.xml_objects['first_vbd']) ## Ref new_vbd[0][1][1].text = new_vbd_ref ## UUID new_vbd[0][2][1][0][0][1].text = str(uuid()) ## Map the VDI ref new_vbd[0][2][1][0][4][1].text = new_vdi_ref ## Set disk letter and userdevice new_vbd[0][2][1][0][5][1].text = "xvd%s" % chr(self.next_disk_letter + 97) new_vbd[0][2][1][0][6][1].text = str(self.next_disk_letter) ## bootable to false new_vbd[0][2][1][0][7][1][0].text = "0" ## copy a new vdi new_vdi = copy.deepcopy(self.xml_objects['first_vdi']) ## map the VBD ref new_vdi[0][2][1][0][6][1][0][0][0].text = new_vbd_ref ## uuid new_vdi[0][2][1][0][0][1].text = str(uuid()) ## ref new_vdi[0][1][1].text = new_vdi_ref new_vdi[0][2][1][0][8][1].text = str(size) new_vdi[0][2][1][0][9][1].text = str(size) ## name label new_vdi[0][2][1][0][1][1].text = str(self.next_disk_letter) disk = (new_vdi_ref, path , size) self.disks.insert(len(self.disks) -1,disk) self.xml_objects['objects_array'].append(new_vbd) self.xml_objects['objects_array'].append(new_vdi) new_vbd_value = copy.deepcopy(self.xml_objects['VBD_config'][0][0][0]) new_vbd_value.text = new_vbd_ref self.xml_objects['VBD_config'][0][0].append(new_vbd_value) self.next_disk_letter = self.next_disk_letter + 1 if self.next_disk_letter == 3: self.next_disk_letter = self.next_disk_letter + 1 def is_hvm(self): self.xml_objects['HVM_boot_policy'].text = "BIOS order" def is_pv(self): self.xml_objects['HVM_boot_policy'].text = "" if self.xml_objects['PV_kernel'].text != "": self.xml_objects['PV_bootloader'].text = "pygrub" else: self.xml_objects['PV_bootloader'].text = "" def set_name(self, name): self.xml_objects['name'].text = name def get_name(self): return self.xml_objects['name'].text def set_memory(self, memory): self.xml_objects['memory_static_max'].text = str(memory) self.xml_objects['memory_dynamic_max'].text = str(memory) self.xml_objects['memory_dynamic_min'].text = str(memory) def get_memory(self): return self.xml_objects['memory_static_max'].text def set_name(self, name): self.xml_objects['name'].text = name def get_name(self): return self.xml_objects['name'].text def set_vcpus(self, vcpus): self.xml_objects['vcpus'].text = str(vcpus) self.xml_objects['vcpus_max'].text = str(vcpus) def get_vcpus(self): return self.xml_objects['vcpus'].text def handle_exception(self): ## this is for when the http connection drops, we try to figure out what happened. if self.conn: try: response = self.conn.getresponse() except: print "Internal Error. Possible problem: Please make sure you have enough space on your default SR" sys.exit(254) if response.status == 401: print "Unauthorised response from server. Exiting" sys.exit(254) elif response.status != 200: print "Server returned error code %d. Exiting" % response.status print "Extra Info: %s" % response.read() sys.exit(254) else: print "Error writing file. Exiting" sys.exit(254) def save_as(self, filename=None, sparse=False, username=None, password=None, server=None, ssl=True, sftp=False): self.print_report() if server: print "Connecting to target %s" % server import base64 auth = base64.encodestring("%s:%s" % (username, password)).strip() import httplib if ssl: conn = httplib.HTTPSConnection(server) else: conn = httplib.HTTPConnection(server) headers = {"Authorization" : "Basic %s" % auth} conn.request("PUT", "/import", headers=headers) conn.write = conn.send self.conn = conn output_file = tarfile.open(fileobj=conn, mode='w|') else: output_file = tarfile.open(filename, mode='w|') print "Generating XVA file %s" % filename info = tarfile.TarInfo(name="ova.xml") output_xml = ET.tostring(self.tree) string = cStringIO.StringIO(output_xml) string.seek(0) info.size=len(output_xml) try: output_file.addfile(tarinfo=info, fileobj=string) except: self.handle_exception() chunksize=1048576 for disk in self.disks: basefilename=0 input_file = open(disk[1], "rb") input_file.seek(0,os.SEEK_END) input_file_size=input_file.tell() input_file.seek(0) position = 0 print "\nProcessing disk %s(%s bytes)" % (disk[1], input_file_size) read_len = -1 prog = ProgressBar(0, input_file_size, 77, mode='fixed') oldprog = str(prog) while True: input_buffer = input_file.read(chunksize) read_len = len(input_buffer) if read_len == 0 : break force = False if position == 0: force=True if (input_file_size - position) < (chunksize * 2) : force = True position = position + chunksize prog.update_amount(position) if oldprog != str(prog): print prog, "\r", sys.stdout.flush() oldprog=str(prog) input_file.seek(position) zeroes = input_buffer.count('\0') if zeroes == chunksize and not force and sparse: basefilename = basefilename + 1 else: string = cStringIO.StringIO(input_buffer) string.seek(0) info = tarfile.TarInfo(name="%s/%08d" % (disk[0] , basefilename)) info.size=read_len try: output_file.addfile(tarinfo=info, fileobj=string) except: self.handle_exception() hash = sha1(input_buffer).hexdigest() string = cStringIO.StringIO(hash) info = tarfile.TarInfo(name="%s/%08d.checksum" % (disk[0], basefilename)) info.size=40 try: output_file.addfile(tarinfo=info, fileobj=string) except: self.handle_exception() basefilename = basefilename + 1 print "\n" sys.stdout.flush() output_file.close() if server: response = conn.getresponse() if response.status == 200: print "VM Successfully streamed" else: print "VM did not stream successfully, Return code: %d" % response.status if sftp: print "Trying to SFTP your kernel/initrd to %s" % server import paramiko transport = paramiko.Transport((server, 22)) transport.connect(username = username, password = password) sftp_connection = paramiko.SFTPClient.from_transport(transport) try: sftp_connection.mkdir(KERNEL_PREFIX) except: pass try: sftp_connection.mkdir(KERNEL_PREFIX+"/%s" % self.dir_uuid) except Exception, e: print "Oops, UUID Conflict, bailing: %s" % e sys.exit(255) sftp_connection.put(self.local_kernel, self.xml_objects['PV_kernel'].text) if self.local_ramdisk: sftp_connection.put(self.local_ramdisk, self.xml_objects['PV_ramdisk'].text) sftp_connection.close() transport.close() print "Success, However if you have more than 1 node in this pool, copy the %s/%s directory to each of the nodes" % (KERNEL_PREFIX ,self.dir_uuid) if self.dir_uuid and (not server or not sftp): print "With the options you supplied, you will need to SFTP/SCP the kernel/initrd to the server manually" print "Create the %s/%s directory" % (KERNEL_PREFIX, self.dir_uuid) print "Copy %s to %s" % (self.local_kernel, self.xml_objects['PV_kernel'].text) if self.local_ramdisk: print "and copy %s to %s" % (self.local_ramdisk, self.xml_objects['PV_ramdisk'].text) print "Copy these to _all_ the nodes in the pool" if __name__ == "__main__": from optparse import OptionParser from optparse import OptionGroup parser = OptionParser() parser.add_option("-c", "--config", dest="config",default=None, help="Specify the OSS Xen config file to process(all other options output options are ignored)", metavar="FILE") parser.add_option("--sparse", action="store_true", dest="sparse", help="Attempt sparse mode(detecting chunks that are zero)", default=False) params = OptionGroup(parser, "Virtual Machine Parameters", "These options are only read when you dont specify a config file with -c") params.add_option("-d", "--disk", action="append", dest="disks", help="Add disk in file/block device DISK, make sure first disk given is the boot disk", metavar="DISK") params.add_option("-m", "--memory", dest="memory", default=256, type="int", help="Set memory to MEM(Megabytes), default 256", metavar="MEM") params.add_option("-n", "--name", dest="name", default="Unnamed", help="Set VM name to NAME(default unnamed)", metavar="NAME") params.add_option("-v", "--vcpus", dest="vcpus", default=1, type="int", help="Set VCPUS to NUM(default 1)", metavar="NUM") params.add_option("--no-acpi", action="store_true", dest="noacpi", help="ACPI Disabled", default=False) params.add_option("--no-apic", action="store_true", dest="noapic", help="APIC Disabled", default=False) params.add_option("--no-viridian", action="store_true", dest="noviridian", help="Viridian Disabled", default=False) params.add_option("--no-pae", action="store_true", dest="nopae", help="PAE Disabled", default=False) params.add_option("--nx", action="store_true", dest="nx", help="NX enabled(default no)", default=False) params.add_option("--is-hvm", action="store_true", dest="hvm", help="Is HVM VM(defaults to HVM)", default=True) params.add_option("--is-pv", action="store_false", dest="hvm", help="Is PV VM") params.add_option("-k", "--kernel", dest="kernel", default=None, help="Supply VM kernel KERNEL", metavar="KERNEL") params.add_option("-r", "--ramdisk", dest="ramdisk", default=None, help="Supply VM ramdisk RAMDISK", metavar="RAMDISK") params.add_option("-a", "--args", dest="args", default=None, help="Supply VM kernel arguments ARGUMENTS", metavar="ARGUMENTS") params.add_option("-b", "--bootloaderargs", dest="bootloaderargs", default=None, help="Supply bootloader arguments BOOTARGUMENTS", metavar="BOOTARGUMENTS") parser.add_option_group(params) output_options = OptionGroup(parser, "Output Options", "These are the options that dictates where the VM should be saved or streamed to a server. You can either save to a file or stream to a server, not both. " "One of either -f or -s have to be specified") output_options.add_option("-f", "--filename", dest="filename", help="Save XVA to file FILE", metavar="FILE", default=None) output_options.add_option("-s", "--server", dest="server", help="Stream VM to host HOSTNAME", metavar="HOSTNAME", default=None) output_options.add_option("--username", dest="username", help="Use username USERNAME when streaming to remote host", metavar="USERNAME") output_options.add_option("--password", dest="password", help="Use password PASSWORD when streaming to remote host", metavar="PASSWORD") output_options.add_option("--no-ssl", action="store_true", dest="nossl", help="SSL disabled with streaming", default=False) output_options.add_option("--sftp", action="store_true", dest="sftp", help="SFTP the kernel/ramdisk to the server(requires paramiko)", default=False) parser.add_option_group(output_options) (options, args) = parser.parse_args() if not options.server and not options.filename: parser.error("Please specify either a filename or server") if options.server and (not options.username or not options.password): parser.error("Please specify a username and password when streaming") if options.sftp: try: import paramiko except: parser.error("in order to use --sftp you need the paramiko module") machine = Xva() if options.config: params = {} execfile(options.config,params) if params.has_key("name"): machine.set_name( params['name'] ) if params.has_key("vcpus"): machine.set_vcpus( params['vcpus'] ) if params.has_key('kernel'): if params['kernel'].endswith("hvmloader"): machine.is_hvm() else: machine.is_pv() if not machine.set_local_kernel(params['kernel']): parser.error("Error with kernel") else: machine.is_pv() if params.has_key('initrd'): if not machine.set_local_ramdisk(params['initrd']): parser.error("Error with ramdisk") elif params.has_key('ramdisk'): if not machine.set_local_ramdisk(params['ramdisk']): parser.error("Error with ramdisk") if params.has_key('bootloaderargs'): machine.set_bootloader_args(params['bootloaderargs']) if params.has_key('root'): machine.append_args("root=%s" % params['root']) if params.has_key('extra'): machine.append_args( params['extra']) if params.has_key("disk") and len(params['disk']) != 0: for disk in params['disk']: (path, device, mode) = disk.split(",") path_split = path.split(":") path_split.reverse() machine.add_disk(path_split[0]) else: print "You need at least 1 Disk, Exiting" sys.exit(254) if params.has_key("memory"): try: memory = int(params['memory'] ) machine.set_memory( memory * 1024 * 1024) except: print "Could parse memory, setting to 256M" machine.set_memory(268435456) if params.has_key("apic") and params['apic'] == 0: machine.set_apic(False) if params.has_key("acpi") and params['acpi'] == 0: machine.set_acpi(False) if params.has_key("nx") and params['nx'] == 1: machine.set_nx(options.nx) if params.has_key("pae") and params['pae'] == 0: machine.set_pae(False) else: if options.disks: for disk in options.disks: machine.add_disk(disk) else: parser.error("At least one disk needs to be specified") if options.hvm: machine.is_hvm() else: machine.is_pv() machine.set_name(options.name) machine.set_bootloader_args(options.bootloaderargs) machine.set_vcpus(options.vcpus) machine.set_acpi(not options.noacpi) machine.set_apic(not options.noapic) machine.set_nx(options.nx) machine.set_viridian(not options.noviridian) machine.set_pae(not options.nopae) if options.kernel: if not machine.set_local_kernel(options.kernel): parser.error("Error with kernel") if options.ramdisk: if not machine.set_local_ramdisk(options.ramdisk): parser.error("Error with ramdisk") machine.set_args(options.args) memory = (options.memory * 1024 * 1024) machine.set_memory(memory) if options.filename: machine.save_as(filename=options.filename, sparse=options.sparse) else: machine.save_as(server=options.server, username=options.username, password=options.password, ssl= not options.nossl, sparse=options.sparse, sftp=options.sftp)