#!/usr/bin/env python ## Author: David Markey <david.markey@citrix.com>, 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="<value> <struct> <member> <name>version</name> <value> <struct> <member> <name>hostname</name> <value>cheesy-2</value> </member> <member> <name>date</name> <value>2009-12-02</value> </member> <member> <name>product_version</name> <value>5.5.0</value> </member> <member> <name>product_brand</name> <value>XenServer</value> </member> <member> <name>build_number</name> <value>24648p</value> </member> <member> <name>xapi_major</name> <value>1</value> </member> <member> <name>xapi_minor</name> <value>3</value> </member> <member> <name>export_vsn</name> <value>2</value> </member> </struct> </value> </member> <member> <name>objects</name> <value> <array> <data> <value> <struct> <member> <name>class</name> <value>VM</value> </member> <member> <name>id</name> <value>Ref:0</value> </member> <member> <name>snapshot</name> <value> <struct> <member> <name>uuid</name> <value>UUID1</value> </member> <member> <name>allowed_operations</name> <value> <array> <data> <value>export</value> <value>clone</value> <value>copy</value> </data> </array> </value> </member> <member> <name>current_operations</name> <value> <struct> <member> <name>OpaqueRef:NULL</name> <value>export</value> </member> </struct> </value> </member> <member> <name>power_state</name> <value>Halted</value> </member> <member> <name>name_label</name> <value>~Unnamed</value> </member> <member> <name>name_description</name> <value/> </member> <member> <name>user_version</name> <value>1</value> </member> <member> <name>is_a_template</name> <value> <boolean>0</boolean> </value> </member> <member> <name>suspend_VDI</name> <value>OpaqueRef:NULL</value> </member> <member> <name>resident_on</name> <value>OpaqueRef:NULL</value> </member> <member> <name>affinity</name> <value>OpaqueRef:b8c1cff1-2b1a-04c6-cf05-e8f832a0c369</value> </member> <member> <name>memory_target</name> <value>268435456</value> </member> <member> <name>memory_static_max</name> <value>268435456</value> </member> <member> <name>memory_dynamic_max</name> <value>268435456</value> </member> <member> <name>memory_dynamic_min</name> <value>268435456</value> </member> <member> <name>memory_static_min</name> <value>16777216</value> </member> <member> <name>VCPUs_params</name> <value> <struct/> </value> </member> <member> <name>VCPUs_max</name> <value>1</value> </member> <member> <name>VCPUs_at_startup</name> <value>1</value> </member> <member> <name>actions_after_shutdown</name> <value>destroy</value> </member> <member> <name>actions_after_reboot</name> <value>restart</value> </member> <member> <name>actions_after_crash</name> <value>restart</value> </member> <member> <name>consoles</name> <value> <array> <data/> </array> </value> </member> <member> <name>VIFs</name> <value> <array> <data> <value>Ref:1</value> </data> </array> </value> </member> <member> <name>VBDs</name> <value> <array> <data> <value>Ref:3</value> <value>Ref:6</value> </data> </array> </value> </member> <member> <name>crash_dumps</name> <value> <array> <data/> </array> </value> </member> <member> <name>VTPMs</name> <value> <array> <data/> </array> </value> </member> <member> <name>PV_bootloader</name> <value/> </member> <member> <name>PV_kernel</name> <value/> </member> <member> <name>PV_ramdisk</name> <value/> </member> <member> <name>PV_args</name> <value/> </member> <member> <name>PV_bootloader_args</name> <value/> </member> <member> <name>PV_legacy_args</name> <value/> </member> <member> <name>HVM_boot_policy</name> <value>BIOS order</value> </member> <member> <name>HVM_boot_params</name> <value> <struct> <member> <name>order</name> <value>dc</value> </member> </struct> </value> </member> <member> <name>HVM_shadow_multiplier</name> <value> <double>1</double> </value> </member> <member> <name>platform</name> <value> <struct> <member> <name>nx</name> <value>false</value> </member> <member> <name>acpi</name> <value>true</value> </member> <member> <name>apic</name> <value>true</value> </member> <member> <name>pae</name> <value>true</value> </member> <member> <name>viridian</name> <value>true</value> </member> </struct> </value> </member> <member> <name>PCI_bus</name> <value/> </member> <member> <name>other_config</name> <value> <struct> <member> <name>install-methods</name> <value>cdrom</value> </member> <member> <name>mac_seed</name> <value></value> </member> </struct> </value> </member> <member> <name>domid</name> <value>-1</value> </member> <member> <name>domarch</name> <value/> </member> <member> <name>last_boot_CPU_flags</name> <value> <struct/> </value> </member> <member> <name>is_control_domain</name> <value> <boolean>0</boolean> </value> </member> <member> <name>metrics</name> <value>OpaqueRef:NULL</value> </member> <member> <name>guest_metrics</name> <value>OpaqueRef:NULL</value> </member> <member> <name>last_booted_record</name> <value/> </member> <member> <name>recommendations</name> <value><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></value> </member> <member> <name>xenstore_data</name> <value> <struct/> </value> </member> <member> <name>ha_always_run</name> <value> <boolean>0</boolean> </value> </member> <member> <name>ha_restart_priority</name> <value/> </member> <member> <name>is_a_snapshot</name> <value> <boolean>0</boolean> </value> </member> <member> <name>snapshot_of</name> <value>OpaqueRef:NULL</value> </member> <member> <name>snapshots</name> <value> <array> <data/> </array> </value> </member> <member> <name>snapshot_time</name> <value> <dateTime.iso8601>19700101T00:00:00Z</dateTime.iso8601> </value> </member> <member> <name>transportable_snapshot_id</name> <value/> </member> <member> <name>blobs</name> <value> <struct/> </value> </member> <member> <name>tags</name> <value> <array> <data/> </array> </value> </member> <member> <name>blocked_operations</name> <value> <struct/> </value> </member> </struct> </value> </member> </struct> </value> <value> <struct> <member> <name>class</name> <value>VBD</value> </member> <member> <name>id</name> <value>Ref:6</value> </member> <member> <name>snapshot</name> <value> <struct> <member> <name>uuid</name> <value>UUID2</value> </member> <member> <name>allowed_operations</name> <value> <array> <data> <value>attach</value> <value>eject</value> </data> </array> </value> </member> <member> <name>current_operations</name> <value> <struct/> </value> </member> <member> <name>VM</name> <value>Ref:0</value> </member> <member> <name>VDI</name> <value>Ref:7</value> </member> <member> <name>device</name> <value/> </member> <member> <name>userdevice</name> <value>3</value> </member> <member> <name>bootable</name> <value> <boolean>0</boolean> </value> </member> <member> <name>mode</name> <value>RO</value> </member> <member> <name>type</name> <value>CD</value> </member> <member> <name>unpluggable</name> <value> <boolean>1</boolean> </value> </member> <member> <name>storage_lock</name> <value> <boolean>0</boolean> </value> </member> <member> <name>empty</name> <value> <boolean>1</boolean> </value> </member> <member> <name>other_config</name> <value> <struct/> </value> </member> <member> <name>currently_attached</name> <value> <boolean>0</boolean> </value> </member> <member> <name>status_code</name> <value>0</value> </member> <member> <name>status_detail</name> <value/> </member> <member> <name>runtime_properties</name> <value> <struct/> </value> </member> <member> <name>qos_algorithm_type</name> <value/> </member> <member> <name>qos_algorithm_params</name> <value> <struct/> </value> </member> <member> <name>qos_supported_algorithms</name> <value> <array> <data/> </array> </value> </member> <member> <name>metrics</name> <value>OpaqueRef:NULL</value> </member> </struct> </value> </member> </struct> </value> <value> <struct> <member> <name>class</name> <value>VBD</value> </member> <member> <name>id</name> <value>Ref:3</value> </member> <member> <name>snapshot</name> <value> <struct> <member> <name>uuid</name> <value>UUID3</value> </member> <member> <name>allowed_operations</name> <value> <array> <data> <value>attach</value> </data> </array> </value> </member> <member> <name>current_operations</name> <value> <struct/> </value> </member> <member> <name>VM</name> <value>Ref:0</value> </member> <member> <name>VDI</name> <value>Ref:4</value> </member> <member> <name>device</name> <value>xvda</value> </member> <member> <name>userdevice</name> <value>0</value> </member> <member> <name>bootable</name> <value> <boolean>1</boolean> </value> </member> <member> <name>mode</name> <value>RW</value> </member> <member> <name>type</name> <value>Disk</value> </member> <member> <name>unpluggable</name> <value> <boolean>0</boolean> </value> </member> <member> <name>storage_lock</name> <value> <boolean>0</boolean> </value> </member> <member> <name>empty</name> <value> <boolean>0</boolean> </value> </member> <member> <name>other_config</name> <value> <struct> <member> <name>owner</name> <value>true</value> </member> </struct> </value> </member> <member> <name>currently_attached</name> <value> <boolean>0</boolean> </value> </member> <member> <name>status_code</name> <value>0</value> </member> <member> <name>status_detail</name> <value/> </member> <member> <name>runtime_properties</name> <value> <struct/> </value> </member> <member> <name>qos_algorithm_type</name> <value/> </member> <member> <name>qos_algorithm_params</name> <value> <struct/> </value> </member> <member> <name>qos_supported_algorithms</name> <value> <array> <data/> </array> </value> </member> <member> <name>metrics</name> <value>OpaqueRef:NULL</value> </member> </struct> </value> </member> </struct> </value> <value> <struct> <member> <name>class</name> <value>VIF</value> </member> <member> <name>id</name> <value>Ref:1</value> </member> <member> <name>snapshot</name> <value> <struct> <member> <name>uuid</name> <value>UUID4</value> </member> <member> <name>allowed_operations</name> <value> <array> <data> <value>attach</value> </data> </array> </value> </member> <member> <name>current_operations</name> <value> <struct/> </value> </member> <member> <name>device</name> <value>0</value> </member> <member> <name>network</name> <value>Ref:2</value> </member> <member> <name>VM</name> <value>Ref:0</value> </member> <member> <name>MAC</name> <value>00:00:00:00:00:00</value> </member> <member> <name>MTU</name> <value>0</value> </member> <member> <name>other_config</name> <value> <struct/> </value> </member> <member> <name>currently_attached</name> <value> <boolean>0</boolean> </value> </member> <member> <name>status_code</name> <value>0</value> </member> <member> <name>status_detail</name> <value/> </member> <member> <name>runtime_properties</name> <value> <struct/> </value> </member> <member> <name>qos_algorithm_type</name> <value/> </member> <member> <name>qos_algorithm_params</name> <value> <struct/> </value> </member> <member> <name>qos_supported_algorithms</name> <value> <array> <data/> </array> </value> </member> <member> <name>metrics</name> <value>OpaqueRef:NULL</value> </member> <member> <name>MAC_autogenerated</name> <value> <boolean>1</boolean> </value> </member> </struct> </value> </member> </struct> </value> <value> <struct> <member> <name>class</name> <value>network</value> </member> <member> <name>id</name> <value>Ref:2</value> </member> <member> <name>snapshot</name> <value> <struct> <member> <name>uuid</name> <value>UUID5</value> </member> <member> <name>name_label</name> <value>Pool-wide network associated with eth0</value> </member> <member> <name>name_description</name> <value/> </member> <member> <name>allowed_operations</name> <value> <array> <data/> </array> </value> </member> <member> <name>current_operations</name> <value> <struct/> </value> </member> <member> <name>VIFs</name> <value> <array> <data> <value>Ref:1</value> </data> </array> </value> </member> <member> <name>PIFs</name> <value> <array> <data/> </array> </value> </member> <member> <name>other_config</name> <value> <struct/> </value> </member> <member> <name>bridge</name> <value>xenbr0</value> </member> <member> <name>blobs</name> <value> <struct/> </value> </member> <member> <name>tags</name> <value> <array> <data/> </array> </value> </member> </struct> </value> </member> </struct> </value> <value> <struct> <member> <name>class</name> <value>VDI</value> </member> <member> <name>id</name> <value>Ref:7</value> </member> <member> <name>snapshot</name> <value> <struct> <member> <name>uuid</name> <value>UUID6</value> </member> <member> <name>name_label</name> <value>IDE 0.0</value> </member> <member> <name>name_description</name> <value/> </member> <member> <name>allowed_operations</name> <value> <array> <data/> </array> </value> </member> <member> <name>current_operations</name> <value> <struct/> </value> </member> <member> <name>SR</name> <value>Ref:8</value> </member> <member> <name>VBDs</name> <value> <array> <data> <value>Ref:6</value> </data> </array> </value> </member> <member> <name>crash_dumps</name> <value> <array> <data/> </array> </value> </member> <member> <name>virtual_size</name> <value>4294965248</value> </member> <member> <name>physical_utilisation</name> <value>4294965248</value> </member> <member> <name>type</name> <value>user</value> </member> <member> <name>sharable</name> <value> <boolean>0</boolean> </value> </member> <member> <name>read_only</name> <value> <boolean>1</boolean> </value> </member> <member> <name>other_config</name> <value> <struct/> </value> </member> <member> <name>storage_lock</name> <value> <boolean>0</boolean> </value> </member> <member> <name>location</name> <value>/dev/xapi/cd/hda</value> </member> <member> <name>managed</name> <value> <boolean>1</boolean> </value> </member> <member> <name>missing</name> <value> <boolean>0</boolean> </value> </member> <member> <name>parent</name> <value>OpaqueRef:NULL</value> </member> <member> <name>xenstore_data</name> <value> <struct/> </value> </member> <member> <name>sm_config</name> <value> <struct> <member> <name>hotplugged_at</name> <value>2010-02-10T10:39:52Z</value> </member> </struct> </value> </member> <member> <name>is_a_snapshot</name> <value> <boolean>0</boolean> </value> </member> <member> <name>snapshot_of</name> <value>OpaqueRef:NULL</value> </member> <member> <name>snapshots</name> <value> <array> <data/> </array> </value> </member> <member> <name>snapshot_time</name> <value> <dateTime.iso8601>19700101T00:00:00Z</dateTime.iso8601> </value> </member> <member> <name>tags</name> <value> <array> <data/> </array> </value> </member> </struct> </value> </member> </struct> </value> <value> <struct> <member> <name>class</name> <value>VDI</value> </member> <member> <name>id</name> <value>Ref:4</value> </member> <member> <name>snapshot</name> <value> <struct> <member> <name>uuid</name> <value>UUID7</value> </member> <member> <name>name_label</name> <value>0</value> </member> <member> <name>name_description</name> <value/> </member> <member> <name>allowed_operations</name> <value> <array> <data> <value>clone</value> <value>destroy</value> <value>resize</value> </data> </array> </value> </member> <member> <name>current_operations</name> <value> <struct/> </value> </member> <member> <name>SR</name> <value>Ref:5</value> </member> <member> <name>VBDs</name> <value> <array> <data> <value>Ref:3</value> </data> </array> </value> </member> <member> <name>crash_dumps</name> <value> <array> <data/> </array> </value> </member> <member> <name>virtual_size</name> <value>5368709120</value> </member> <member> <name>physical_utilisation</name> <value>5385486336</value> </member> <member> <name>type</name> <value>system</value> </member> <member> <name>sharable</name> <value> <boolean>0</boolean> </value> </member> <member> <name>read_only</name> <value> <boolean>0</boolean> </value> </member> <member> <name>other_config</name> <value> <struct/> </value> </member> <member> <name>storage_lock</name> <value> <boolean>0</boolean> </value> </member> <member> <name>location</name> <value>ebe16ffc-7f5d-4761-9cfe-2f052577d64d</value> </member> <member> <name>managed</name> <value> <boolean>1</boolean> </value> </member> <member> <name>missing</name> <value> <boolean>0</boolean> </value> </member> <member> <name>parent</name> <value>OpaqueRef:NULL</value> </member> <member> <name>xenstore_data</name> <value> <struct/> </value> </member> <member> <name>sm_config</name> <value> <struct> <member> <name>vdi_type</name> <value>vhd</value> </member> </struct> </value> </member> <member> <name>is_a_snapshot</name> <value> <boolean>0</boolean> </value> </member> <member> <name>snapshot_of</name> <value>OpaqueRef:NULL</value> </member> <member> <name>snapshots</name> <value> <array> <data/> </array> </value> </member> <member> <name>snapshot_time</name> <value> <dateTime.iso8601>19700101T00:00:00Z</dateTime.iso8601> </value> </member> <member> <name>tags</name> <value> <array> <data/> </array> </value> </member> </struct> </value> </member> </struct> </value> <value> <struct> <member> <name>class</name> <value>SR</value> </member> <member> <name>id</name> <value>Ref:5</value> </member> <member> <name>snapshot</name> <value> <struct> <member> <name>uuid</name> <value>UUID8</value> </member> <member> <name>name_label</name> <value>Local storage</value> </member> <member> <name>name_description</name> <value/> </member> <member> <name>allowed_operations</name> <value> <array> <data> <value>forget</value> <value>vdi_create</value> <value>vdi_snapshot</value> <value>plug</value> <value>update</value> <value>destroy</value> <value>vdi_destroy</value> <value>scan</value> <value>vdi_clone</value> <value>vdi_resize</value> <value>unplug</value> </data> </array> </value> </member> <member> <name>current_operations</name> <value> <struct/> </value> </member> <member> <name>VDIs</name> <value> <array> <data> <value>Ref:4</value> </data> </array> </value> </member> <member> <name>PBDs</name> <value> <array> <data/> </array> </value> </member> <member> <name>virtual_allocation</name> <value>39313211392</value> </member> <member> <name>physical_utilisation</name> <value>39325794304</value> </member> <member> <name>physical_size</name> <value>71777124352</value> </member> <member> <name>type</name> <value>lvm</value> </member> <member> <name>content_type</name> <value>user</value> </member> <member> <name>shared</name> <value> <boolean>0</boolean> </value> </member> <member> <name>other_config</name> <value> <struct> <member> <name>i18n-original-value-name_label</name> <value>Local storage</value> </member> <member> <name>i18n-key</name> <value>local-storage</value> </member> </struct> </value> </member> <member> <name>tags</name> <value> <array> <data/> </array> </value> </member> <member> <name>sm_config</name> <value> <struct> <member> <name>allocation</name> <value>thick</value> </member> <member> <name>use_vhd</name> <value>true</value> </member> <member> <name>devserial</name> <value>scsi-SATA_WDC_WD800JD-75M_WD-WMAM9AAE1521</value> </member> </struct> </value> </member> <member> <name>blobs</name> <value> <struct/> </value> </member> </struct> </value> </member> </struct> </value> <value> <struct> <member> <name>class</name> <value>SR</value> </member> <member> <name>id</name> <value>Ref:8</value> </member> <member> <name>snapshot</name> <value> <struct> <member> <name>uuid</name> <value>UUID9</value> </member> <member> <name>name_label</name> <value>DVD drives</value> </member> <member> <name>name_description</name> <value>Physical DVD drives</value> </member> <member> <name>allowed_operations</name> <value> <array> <data> <value>forget</value> <value>vdi_introduce</value> <value>plug</value> <value>update</value> <value>destroy</value> <value>scan</value> <value>vdi_clone</value> <value>unplug</value> </data> </array> </value> </member> <member> <name>current_operations</name> <value> <struct/> </value> </member> <member> <name>VDIs</name> <value> <array> <data> <value>Ref:7</value> </data> </array> </value> </member> <member> <name>PBDs</name> <value> <array> <data/> </array> </value> </member> <member> <name>virtual_allocation</name> <value>4294965248</value> </member> <member> <name>physical_utilisation</name> <value>4294965248</value> </member> <member> <name>physical_size</name> <value>4294965248</value> </member> <member> <name>type</name> <value>udev</value> </member> <member> <name>content_type</name> <value>iso</value> </member> <member> <name>shared</name> <value> <boolean>0</boolean> </value> </member> <member> <name>other_config</name> <value> <struct> <member> <name>i18n-original-value-name_description</name> <value>Physical DVD drives</value> </member> <member> <name>i18n-original-value-name_label</name> <value>DVD drives</value> </member> <member> <name>i18n-key</name> <value>local-hotplug-cd</value> </member> </struct> </value> </member> <member> <name>tags</name> <value> <array> <data/> </array> </value> </member> <member> <name>sm_config</name> <value> <struct> <member> <name>type</name> <value>cd</value> </member> </struct> </value> </member> <member> <name>blobs</name> <value> <struct/> </value> </member> </struct> </value> </member> </struct> </value> </data> </array> </value> </member> </struct></value>" ## 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)