[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [XEND] Updates to SR and VDI implementations
# HG changeset patch # User Alastair Tse <atse@xxxxxxxxxxxxx> # Node ID 91c7ee18c978f7327487b831b9b07428bae80675 # Parent 58521d4b7c7b6fae9fd3f82345d0e8df9e7ba0a1 [XEND] Updates to SR and VDI implementations * Moved xenapi transport util method, stringify to xen.util.xmlrpclib2 * XenVDI now preserves configuration to an XML-ish file * Update Xen API's class names to be all lowercase * Update get_by_label to get_by_name_label and return sets as the API expects. * Add support for VBD creation with a VDI reference. Signed-off-by: Alastair Tse <atse@xxxxxxxxxxxxx> --- tools/python/scripts/xapi.py | 67 +++++++- tools/python/scripts/xapi.vdicfg.py | 7 tools/python/xen/util/xmlrpclib2.py | 26 ++- tools/python/xen/xend/XendAPI.py | 206 +++++++++++++++++-------- tools/python/xen/xend/XendConfig.py | 18 ++ tools/python/xen/xend/XendDomainInfo.py | 27 +++ tools/python/xen/xend/XendStorageRepository.py | 133 ++++++++++++---- tools/python/xen/xend/XendVDI.py | 111 ++++++++++++- 8 files changed, 477 insertions(+), 118 deletions(-) diff -r 58521d4b7c7b -r 91c7ee18c978 tools/python/scripts/xapi.py --- a/tools/python/scripts/xapi.py Thu Oct 12 18:51:17 2006 +0100 +++ b/tools/python/scripts/xapi.py Fri Oct 13 15:13:21 2006 +0100 @@ -21,15 +21,23 @@ from pprint import pprint from pprint import pprint from types import DictType +MB = 1024 * 1024 + HOST_INFO_FORMAT = '%-20s: %-50s' VM_LIST_FORMAT = '%(name_label)-18s %(memory_actual)-5s %(vcpus_number)-5s'\ - ' %(power_state)-12s %(uuid)-32s' - + ' %(power_state)-12s %(uuid)-36s' +SR_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(physical_size)-10s' \ + '%(type)-10s' +VDI_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(virtual_size)-8s '\ + '%(sector_size)-8s' LOGIN = ('atse', 'passwd') COMMANDS = { 'host-info': ('', 'Get Xen Host Info'), + 'sr-list': ('', 'List all SRs'), 'vbd-create': ('<domname> <pycfg>', 'Create VBD attached to domname'), + 'vdi-list' : ('', 'List all VDI'), + 'vdi-delete': ('<vdi_uuid>', 'Delete VDI'), 'vif-create': ('<domname> <pycfg>', 'Create VIF attached to domname'), 'vm-create': ('<pycfg>', 'Create VM with python config'), @@ -84,8 +92,8 @@ def execute(fn, *args): def _connect(*args): server = ServerProxy('httpu:///var/run/xend/xmlrpc.sock') - session = execute(server.Session.login_with_password, *LOGIN) - host = execute(server.Session.get_this_host, session) + session = execute(server.session.login_with_password, *LOGIN) + host = execute(server.session.get_this_host, session) return (server, session) def _stringify(adict): @@ -248,8 +256,55 @@ def xapi_vif_create(*args): vif_uuid = execute(server.VIF.create, session, cfg) print 'Done. (%s)' % vif_uuid - - +def xapi_vdi_list(*args): + server, session = _connect() + vdis = execute(server.VDI.get_all, session) + + print VDI_LIST_FORMAT % {'name_label': 'VDI Label', + 'uuid' : 'UUID', + 'virtual_size': 'Sectors', + 'sector_size': 'Sector Size'} + + for vdi in vdis: + vdi_struct = execute(server.VDI.get_record, session, vdi) + print VDI_LIST_FORMAT % vdi_struct + +def xapi_sr_list(*args): + server, session = _connect() + srs = execute(server.SR.get_all, session) + print SR_LIST_FORMAT % {'name_label': 'SR Label', + 'uuid' : 'UUID', + 'physical_size': 'Size', + 'type': 'Type'} + for sr in srs: + sr_struct = execute(server.SR.get_record, session, sr) + sr_struct['physical_size'] = int(sr_struct['physical_size'])/MB + print SR_LIST_FORMAT % sr_struct + +def xapi_vdi_create(*args): + server, session = _connect() + cfg = _read_python_cfg(args[0]) + + srs = execute(server.SR.get_all, session) + sr = srs[0] + cfg['SR'] = sr + + size = (cfg['virtual_size'] * cfg['sector_size'])/MB + print 'Creating VDI of size: %dMB' % size + uuid = execute(server.VDI.create, session, cfg) + print 'Done. (%s)' % uuid + +def xapi_vdi_delete(*args): + server, session = _connect() + if len(args) < 1: + raise OptionError('Not enough arguments') + + vdi_uuid = args[0] + print 'Deleting VDI %s' % vdi_uuid + result = execute(server.VDI.destroy, session, vdi_uuid) + print 'Done.' + + # # Command Line Utils # diff -r 58521d4b7c7b -r 91c7ee18c978 tools/python/xen/util/xmlrpclib2.py --- a/tools/python/xen/util/xmlrpclib2.py Thu Oct 12 18:51:17 2006 +0100 +++ b/tools/python/xen/util/xmlrpclib2.py Fri Oct 13 15:13:21 2006 +0100 @@ -21,8 +21,9 @@ An enhanced XML-RPC client/server interf """ import string -import types import fcntl +from types import * + from httplib import HTTPConnection, HTTP from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler @@ -38,6 +39,23 @@ except ImportError: # SSHTransport is disabled on Python <2.4, because it uses the subprocess # package. ssh_enabled = False + +# +# Convert all integers to strings as described in the Xen API +# + + +def stringify(value): + if isinstance(value, IntType) and not isinstance(value, BooleanType): + return str(value) + elif isinstance(value, DictType): + for k, v in value.items(): + value[k] = stringify(v) + return value + elif isinstance(value, (TupleType, ListType)): + return [stringify(v) for v in value] + else: + return value # A new ServerProxy that also supports httpu urls. An http URL comes in the @@ -91,8 +109,7 @@ class UnixTransport(xmlrpclib.Transport) # See _marshalled_dispatch below. def conv_string(x): - if (isinstance(x, types.StringType) or - isinstance(x, unicode)): + if isinstance(x, StringTypes): s = string.replace(x, "'", r"\047") exec "s = '" + s + "'" return s @@ -169,8 +186,7 @@ class TCPXMLRPCServer(SocketServer.Threa # to transmit the string using Python encoding. # Thanks to David Mertz <mertz@xxxxxxxxx> for the trick (buried # in xml_pickle.py). - if (isinstance(response, types.StringType) or - isinstance(response, unicode)): + if isinstance(response, StringTypes): response = repr(response)[1:-1] response = (response,) diff -r 58521d4b7c7b -r 91c7ee18c978 tools/python/xen/xend/XendAPI.py --- a/tools/python/xen/xend/XendAPI.py Thu Oct 12 18:51:17 2006 +0100 +++ b/tools/python/xen/xend/XendAPI.py Fri Oct 13 15:13:21 2006 +0100 @@ -25,23 +25,10 @@ from xen.xend.XendLogging import log from xen.xend.XendLogging import log from xen.xend.XendAPIConstants import * - -from types import * - -def _stringify(value): - if isinstance(value, IntType) and not isinstance(value, BooleanType): - return str(value) - elif isinstance(value, DictType): - for k, v in value.items(): - value[k] = _stringify(v) - return value - elif isinstance(value, (TupleType, ListType)): - return [_stringify(v) for v in value] - else: - return value - +from xen.util.xmlrpclib2 import stringify + def xen_api_success(value): - return {"Status": "Success", "Value": _stringify(value)} + return {"Status": "Success", "Value": stringify(value)} def xen_api_success_void(): """Return success, but caller expects no return value.""" @@ -252,7 +239,7 @@ class XendAPI: """ classes = { - 'Session': (session_required,), + 'session': (session_required,), 'host': (valid_host, session_required), 'host_cpu': (valid_host_cpu, session_required), 'VM': (valid_vm, session_required), @@ -346,9 +333,9 @@ class XendAPI: # ---------------------------------------------------------------- # NOTE: Left unwrapped by __init__ - Session_attr_ro = ['this_host', 'this_user'] - Session_methods = ['logout'] - # Session_funcs = ['login_with_password'] + session_attr_ro = ['this_host', 'this_user'] + session_methods = ['logout'] + # session_funcs = ['login_with_password'] def session_login_with_password(self, username, password): try: @@ -356,7 +343,7 @@ class XendAPI: return xen_api_success(session) except XendError, e: return xen_api_error(XEND_ERROR_AUTHENTICATION_FAILED) - session_login_with_password.api = 'Session.login_with_password' + session_login_with_password.api = 'session.login_with_password' # object methods @@ -405,7 +392,7 @@ class XendAPI: 'reboot', 'shutdown'] - host_funcs = ['get_by_label'] + host_funcs = ['get_by_name_label'] # attributes def host_get_name_label(self, session, host_ref): @@ -572,7 +559,7 @@ class XendAPI: 'suspend', 'resume'] - VM_funcs = ['get_by_label'] + VM_funcs = ['get_by_name_label'] # parameters required for _create() VM_attr_inst = [ @@ -892,7 +879,8 @@ class XendAPI: def vm_get_all(self, session): refs = [d.get_uuid() for d in XendDomain.instance().list()] return xen_api_success(refs) - def vm_get_by_label(self, session, label): + + def vm_get_by_name_label(self, session, label): xendom = XendDomain.instance() dom = xendom.domain_lookup_nr(label) if dom: @@ -1022,16 +1010,27 @@ class XendAPI: # class methods def vbd_create(self, session, vbd_struct): xendom = XendDomain.instance() - if xendom.is_valid_vm(vbd_struct['VM']): - dom = xendom.get_vm_by_uuid(vbd_struct['VM']) - try: + if not xendom.is_valid_vm(vbd_struct['VM']): + return xen_api_error(XEND_ERROR_DOMAIN_INVALID) + + dom = xendom.get_vm_by_uuid(vbd_struct['VM']) + vbd_ref = '' + try: + if vbd_struct.get('VDI', None): + # this is a traditional VBD without VDI and SR vbd_ref = dom.create_vbd(vbd_struct) - xendom.managed_config_save(dom) - return xen_api_success(vbd_ref) - except XendError: - return xen_api_error(XEND_ERROR_TODO) - else: - return xen_api_error(XEND_ERROR_DOMAIN_INVALID) + else: + # new VBD via VDI/SR + vdi_ref = vbd_struct.get('VDI') + sr = XendNode.instance().get_sr() + vdi_image = sr.xen_api_get_by_uuid(vdi_ref) + vdi_image_path = vdi_image.image_path + vbd_ref = dom.create_vbd_with_vdi(vbd_struct, vdi_image_path) + except XendError: + return xen_api_todo() + + xendom.managed_config_save(dom) + return xen_api_success(vbd_ref) # attributes (rw) def vbd_get_vm(self, session, vbd_ref): @@ -1118,61 +1117,144 @@ class XendAPI: VDI_attr_inst = VDI_attr_ro + VDI_attr_rw VDI_methods = ['snapshot'] - VDI_funcs = ['get_by_label'] + VDI_funcs = ['get_by_name_label'] + def vdi_get_vbds(self, session, vdi_ref): return xen_api_todo() + def vdi_get_physical_utilisation(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + return xen_api_success(image.get_physical_utilisation()) + def vdi_get_sector_size(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + return xen_api_success(image.sector_size) + def vdi_get_type(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + return xen_api_success(image.type) + def vdi_get_parent(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + return xen_api_success(image.parent) + def vdi_get_children(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + return xen_api_success(image.children) + def vdi_get_name_label(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + return xen_api_success(image.name_label) + def vdi_get_name_description(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + return xen_api_success(image.name_description) + def vdi_get_sr(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + return xen_api_success(sr.uuid) + def vdi_get_virtual_size(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + return xen_api_success(image.virtual_size) + def vdi_get_sharable(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + return xen_api_success(image.sharable) + def vdi_get_read_only(self, session, vdi_ref): - return xen_api_todo() - def vdi_get_uuid(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + return xen_api_success(image.sharable) + def vdi_set_name_label(self, session, vdi_ref, value): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + image.name_label = value + return xen_api_success_void() + def vdi_set_name_description(self, session, vdi_ref, value): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + image.name_description = value + return xen_api_success_void() + def vdi_set_sr(self, session, vdi_ref, value): - return xen_api_todo() + return xen_api_error(XEND_ERROR_UNSUPPORTED) + def vdi_set_virtual_size(self, session, vdi_ref, value): - return xen_api_todo() + return xen_api_error(XEND_ERROR_UNSUPPORTED) + def vdi_set_sharable(self, session, vdi_ref, value): return xen_api_todo() def vdi_set_read_only(self, session, vdi_ref, value): return xen_api_todo() + + # Object Methods def vdi_snapshot(self, session, vdi_ref): return xen_api_todo() + def vdi_destroy(self, session, vdi_ref): - return xen_api_todo() + sr = XendNode.instance().get_sr() + sr.destroy_image(vdi_ref) + return xen_api_success_void() + def vdi_to_xml(self, session, vdi_ref): return xen_api_todo() + def vdi_get_record(self, session, vdi_ref): - return xen_api_todo() - def vdi_create(self, session): - return xen_api_todo() - def vdi_get_by_uuid(self, session): - return xen_api_todo() + sr = XendNode.instance().get_sr() + image = sr.xen_api_get_by_uuid(vdi_ref) + if image: + return xen_api_success({ + 'uuid': vdi_ref, + 'name_label': image.name_label, + 'name_description': image.name_description, + 'SR': sr.uuid, + 'VBDs': [], # TODO + 'virtual_size': image.virtual_size, + 'physical_utilisation': image.physical_utilisation, + 'sector_size': image.sector_size, + 'type': image.type, + 'parent': image.parent, + 'children': image.children, + 'sharable': image.sharable, + 'read_only': image.read_only, + }) + + return xen_api_error(XEND_ERROR_VDI_INVALID) + + # Class Functions + def vdi_create(self, session, vdi_struct): + sr = XendNode.instance().get_sr() + sr_ref = vdi_struct['SR'] + if sr.uuid != sr_ref: + return xen_api_error(XEND_ERROR_SR_INVALID) + + vdi_uuid = sr.create_image(vdi_struct) + return xen_api_success(vdi_uuid) + def vdi_get_all(self, session): - return xen_api_todo() - def vdi_get_by_label(self, session): - return xen_api_todo() + sr = XendNode.instance().get_sr() + return xen_api_success(sr.list_images()) + + def vdi_get_by_name_label(self, session, name): + sr = XendNode.instance().get_sr() + image_uuid = sr.xen_api_get_by_name_label(name) + if image_uuid: + return xen_api_success(image_uuid) + + return xen_api_error(XEND_ERROR_VDI_INVALID) + # Xen API: Class SR # ---------------------------------------------------------------- @@ -1193,14 +1275,14 @@ class XendAPI: 'name_description'] SR_methods = ['clone'] - SR_funcs = ['get_by_label'] + SR_funcs = ['get_by_name_label'] # Class Functions def sr_get_all(self, session): sr = XendNode.instance().get_sr() return xen_api_success([sr.uuid]) - def sr_get_by_label(self, session, label): + def sr_get_by_name_label(self, session, label): sr = XendNode.instance().get_sr() if sr.name_label != label: return xen_api_error(XEND_ERROR_SR_INVALID) diff -r 58521d4b7c7b -r 91c7ee18c978 tools/python/xen/xend/XendConfig.py --- a/tools/python/xen/xend/XendConfig.py Thu Oct 12 18:51:17 2006 +0100 +++ b/tools/python/xen/xend/XendConfig.py Fri Oct 13 15:13:21 2006 +0100 @@ -566,7 +566,7 @@ class XendConfig(dict): for dev_uuid, (dev_type, dev_info) in cfg['device'].items(): if dev_type == 'vif': cfg['vif_refs'].append(dev_uuid) - elif dev_type == 'vbd': + elif dev_type in ('vbd','tap'): cfg['vbd_refs'].append(dev_uuid) return cfg @@ -771,6 +771,8 @@ class XendConfig(dict): self['device'][dev_uuid] = (dev_type, dev_info) if dev_type in ('vif', 'vbd'): self['%s_refs' % dev_type].append(dev_uuid) + elif dev_type in ('tap',): + self['vbd_refs'].append(dev_uuid) return dev_uuid if cfg_xenapi: @@ -805,7 +807,21 @@ class XendConfig(dict): self['device'][dev_uuid] = (dev_type, dev_info) self['vbd_refs'].append(dev_uuid) return dev_uuid + + elif dev_type == 'tap': + dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image') + dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device') + if cfg_xenapi.get('mode') == 'RW': + dev_info['mode'] = 'w' + else: + dev_info['mode'] = 'r' + + dev_uuid = cfg_xenapi.get('uuid', uuid.createString()) + dev_info['uuid'] = dev_uuid + self['device'][dev_uuid] = (dev_type, dev_info) + self['vbd_refs'].append(dev_uuid) + return dev_uuid return '' diff -r 58521d4b7c7b -r 91c7ee18c978 tools/python/xen/xend/XendDomainInfo.py --- a/tools/python/xen/xend/XendDomainInfo.py Thu Oct 12 18:51:17 2006 +0100 +++ b/tools/python/xen/xend/XendDomainInfo.py Fri Oct 13 15:13:21 2006 +0100 @@ -1826,8 +1826,33 @@ class XendDomainInfo: return dev_uuid + def create_vbd_with_vdi(self, xenapi_vbd, vdi_image_path): + """Create a VBD using a VDI from XendStorageRepository. + + @param xenapi_vbd: vbd struct from the Xen API + @param vdi_image_path: VDI UUID + @rtype: string + @return: uuid of the device + """ + xenapi_vbd['image'] = vdi_image_path + dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd) + if not dev_uuid: + raise XendError('Failed to create device') + + if self.state in (XEN_API_VM_POWER_STATE_RUNNING,): + sxpr = self.info.device_sxpr(dev_uuid) + devid = self.getDeviceController('tap').createDevice(sxpr) + raise XendError("Device creation failed") + + return dev_uuid + def create_vif(self, xenapi_vif): - + """Create VIF device from the passed struct in Xen API format. + + @param xenapi_vif: Xen API VIF Struct. + @rtype: string + @return: UUID + """ dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif) if not dev_uuid: raise XendError('Failed to create device') diff -r 58521d4b7c7b -r 91c7ee18c978 tools/python/xen/xend/XendStorageRepository.py --- a/tools/python/xen/xend/XendStorageRepository.py Thu Oct 12 18:51:17 2006 +0100 +++ b/tools/python/xen/xend/XendStorageRepository.py Fri Oct 13 15:13:21 2006 +0100 @@ -31,7 +31,8 @@ XEND_STORAGE_DIR = "/var/lib/xend/storag XEND_STORAGE_DIR = "/var/lib/xend/storage/" XEND_STORAGE_QCOW_FILENAME = "%s.qcow" XEND_STORAGE_IMG_FILENAME = "%s.img" -DF_COMMAND = "df -kl" +XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml" +DF_COMMAND = "df -lP" QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create %d %s %s" KB = 1024 @@ -55,7 +56,7 @@ class XendStorageRepository: """ @keyword storage_dir: Where the images will be stored. @type storage_dir: string - @keyword storage_max: Maximum disk space to use in KB. + @keyword storage_max: Maximum disk space to use in bytes. @type storage_max: int @ivar storage_free: storage space free for this repository @@ -82,7 +83,7 @@ class XendStorageRepository: def _sr_uuid(self): uuid_file = os.path.join(XEND_STORAGE_DIR, 'uuid') try: - if os.path.exists(uuid_file): + if uuid_file and os.path.exists(uuid_file): return open(uuid_file, 'r').read().strip() else: new_uuid = uuid.createString() @@ -114,16 +115,25 @@ class XendStorageRepository: if image_uuid not in self.images: image_file = XEND_STORAGE_IMG_FILENAME % image_uuid qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid - image_path = os.path.join(XEND_STORAGE_DIR, - image_file) + cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid + + image_path = os.path.join(XEND_STORAGE_DIR,image_file) qcow_path = os.path.join(XEND_STORAGE_DIR, qcow_file) - image_size_kb = (os.stat(image_path).st_size)/1024 + cfg_path = os.path.join(XEND_STORAGE_DIR, cfg_file) + + qcow_size = os.stat(qcow_path).st_size + image_size = os.stat(image_path).st_size vdi = XendQCOWVDI(image_uuid, self.uuid, - qcow_path, image_path, - image_size_kb, image_size_kb) + qcow_path, image_path, cfg_path, + image_size, + qcow_size + image_size) + + if cfg_path and os.path.exists(cfg_path): + vdi.load_config(cfg_path) + self.images[image_uuid] = vdi - total_used += image_size_kb + total_used += image_size # remove images that aren't valid for image_uuid in self.images.keys(): @@ -147,7 +157,7 @@ class XendStorageRepository: def _get_df(self): """Returns the output of 'df' in a dictionary where the keys are the Linux device numbers, and the values are it's corresponding - free space in KB. + free space in bytes @rtype: dictionary """ @@ -162,7 +172,7 @@ class XendStorageRepository: return devnum_free def _get_free_space(self): - """Returns the amount of free space in KB available in the storage + """Returns the amount of free space in bytes available in the storage partition. Note that this may not be used if the storage repository is initialised with a maximum size in storage_max. @@ -175,7 +185,7 @@ class XendStorageRepository: raise DeviceInvalidError("Device not found for storage path: %s" % self.storage_dir) - def _has_space_available_for(self, size_kb): + def _has_space_available_for(self, size_bytes): """Returns whether there is enough space for an image in the partition which the storage_dir resides on. @@ -184,15 +194,15 @@ class XendStorageRepository: if self.storage_max != -1: return self.storage_free - kb_free = self._get_free_space() - try: - if size_kb < kb_free: + bytes_free = self._get_free_space() + try: + if size_bytes < bytes_free: return True except DeviceInvalidError: pass return False - def create_image(self, desired_size_kb): + def _create_image_files(self, desired_size_bytes): """Create an image and return its assigned UUID. @param desired_size_kb: Desired image size in KB. @@ -204,23 +214,28 @@ class XendStorageRepository: """ self.lock.acquire() try: - if not self._has_space_available_for(desired_size_kb): + if not self._has_space_available_for(desired_size_bytes): raise XendError("Not enough space") image_uuid = uuid.createString() # create file based image image_path = os.path.join(XEND_STORAGE_DIR, XEND_STORAGE_IMG_FILENAME % image_uuid) - block = '\x00' * 1024 + + if image_path and os.path.exists(image_path): + raise XendError("Image with same UUID alreaady exists:" % + image_uuid) + + block = '\x00' * KB img = open(image_path, 'w') - for i in range(desired_size_kb): + for i in range(desired_size_bytes/KB): img.write(block) img.close() # TODO: create qcow image qcow_path = os.path.join(XEND_STORAGE_DIR, XEND_STORAGE_QCOW_FILENAME % image_uuid) - cmd = QCOW_CREATE_COMMAND % (desired_size_kb/1024, + cmd = QCOW_CREATE_COMMAND % (desired_size_bytes/MB, qcow_path, image_path) rc, output = commands.getstatusoutput(cmd) @@ -233,7 +248,7 @@ class XendStorageRepository: return image_uuid finally: self.lock.release() - + def destroy_image(self, image_uuid): """Destroy an image that is managed by this storage repository. @@ -247,9 +262,12 @@ class XendStorageRepository: # TODO: check if it is being used? qcow_path = self.images[image_uuid].qcow_path image_path = self.images[image_uuid].image_path + cfg_path = self.images[image_uuid].cfg_path try: os.unlink(qcow_path) os.unlink(image_path) + if cfg_path and os.path.exists(cfg_path): + os.unlink(cfg_path) except OSError: # TODO: log warning pass @@ -272,7 +290,7 @@ class XendStorageRepository: finally: self.lock.release() - def free_space_kb(self): + def free_space_bytes(self): """Returns the amount of available space in KB. @rtype: int """ @@ -282,7 +300,7 @@ class XendStorageRepository: finally: self.lock.release() - def total_space_kb(self): + def total_space_bytes(self): """Returns the total usable space of the storage repo in KB. @rtype: int """ @@ -295,7 +313,7 @@ class XendStorageRepository: finally: self.lock.release() - def used_space_kb(self): + def used_space_bytes(self): """Returns the total amount of space used by this storage repository. @rtype: int """ @@ -308,25 +326,72 @@ class XendStorageRepository: finally: self.lock.release() - def used_space_bytes(self): - return self.used_space_kb() * KB - def free_space_bytes(self): - return self.free_space_kb() * KB - def total_space_bytes(self): - return self.total_space_kb() * KB - def is_valid_vdi(self, vdi_uuid): return (vdi_uuid in self.images) + + def create_image(self, vdi_struct): + image_uuid = None + try: + sector_count = int(vdi_struct.get('virtual_size', 0)) + sector_size = int(vdi_struct.get('sector_size', 1024)) + size_bytes = (sector_count * sector_size) + + image_uuid = self._create_image_files(size_bytes) + image = self.images[image_uuid] + image_cfg = { + 'sector_size': sector_size, + 'virtual_size': sector_count, + 'type': vdi_struct.get('type', 'system'), + 'name_label': vdi_struct.get('name_label', ''), + 'name_description': vdi_struct.get('name_description', ''), + 'sharable': bool(vdi_struct.get('sharable', False)), + 'read_only': bool(vdi_struct.get('read_only', False)), + } + + # load in configuration from vdi_struct + image.load_config_dict(image_cfg) + + # save configuration to file + cfg_filename = XEND_STORAGE_VDICFG_FILENAME % image_uuid + cfg_path = os.path.join(XEND_STORAGE_DIR, cfg_filename) + image.save_config(cfg_path) + + except Exception, e: + # cleanup before raising exception + if image_uuid: + self.destroy_image(image_uuid) + + raise + + return image_uuid + + def xen_api_get_by_label(self, label): + self.lock.acquire() + try: + for image_uuid, val in self.images.values(): + if val.name_label == label: + return image_uuid + return None + finally: + self.lock.release() + + def xen_api_get_by_uuid(self, image_uuid): + self.lock.acquire() + try: + return self.images.get(image_uuid) + finally: + self.lock.release() + # remove everything below this line!! if __name__ == "__main__": xsr = XendStorageRepository() - print 'Free Space: %d MB' % (xsr.free_space_kb()/1024) + print 'Free Space: %d MB' % (xsr.free_space_bytes()/MB) print "Create Image:", - print xsr.create_image(10 * 1024) + print xsr._create_image_files(10 * MB) print 'Delete all images:' for image_uuid in xsr.list_images(): print image_uuid, - xsr.destroy_image(image_uuid) + xsr._destroy_image_files(image_uuid) print diff -r 58521d4b7c7b -r 91c7ee18c978 tools/python/xen/xend/XendVDI.py --- a/tools/python/xen/xend/XendVDI.py Thu Oct 12 18:51:17 2006 +0100 +++ b/tools/python/xen/xend/XendVDI.py Fri Oct 13 15:13:21 2006 +0100 @@ -19,26 +19,119 @@ # Representation of a Xen API VDI # +import os + +from xen.util.xmlrpclib2 import stringify +from xmlrpclib import dumps, loads + KB = 1024 MB = 1024 * 1024 class XendVDI: + """Generic Xen API compatible VDI representation. + + @cvar SAVED_CFG: list of configuration attributes to save. + @cvar SAVED_CFG_INT: list of configurations that should be ints. + """ + + SAVED_CFG = ['name_label', + 'name_description', + 'sector_size', + 'virtual_size', + 'physical_utilisation', + 'parent', + 'children', + 'sharable', + 'read_only'] + + SAVED_CFG_INT = ['sector_size', 'virtual_size', 'physical_utilisation'] + def __init__(self, uuid, sr_uuid): self.uuid = uuid self.sr_uuid = sr_uuid + self.name_label = "" + self.name_description = "" + self.sector_size = 1024 + self.virtual_size = 0 + self.physical_utilisation = 0 + self.parent = None + self.children = [] + self.sharable = False + self.read_only = False + self.type = "system" + + self.cfg_path = None + + def load_config_dict(self, cfg): + """Loads configuration into the object from a dict. + + @param cfg: configuration dict + @type cfg: dict + """ + for key in self.SAVED_CFG: + if key in cfg: + if key in self.SAVED_CFG_INT: + setattr(self, key, int(cfg[key])) + else: + setattr(self, key, cfg[key]) + + def load_config(self, cfg_path): + """Loads configuration from an XMLRPC parameter format. + + @param cfg_path: configuration file path + @type cfg_path: type + @rtype: bool + @return: Successful or not. + """ + try: + cfg, _ = loads(open(cfg_path).read()) + cfg = cfg[0] + self.load_config_dict(cfg) + self.cfg_path = cfg_path + except IOError, e: + return False + + return True + + def save_config(self, cfg_path = None): + """Saves configuration at give path in XMLRPC parameter format. + + If cfg_path is not give, it defaults to the where the VDI + configuration as loaded if it load_config was called. + + @keyword cfg_path: optional configuration file path + @rtype: bool + @return: Successful or not. + """ + try: + if not cfg_path and not self.cfg_path: + return False + + if not cfg_path: + cfg_path = self.cfg_path + + cfg = {} + for key in self.SAVED_CFG: + try: + cfg[key] = getattr(self, key) + except AttributeError: + pass + open(cfg_path, 'w').write(dumps((stringify(cfg),), + allow_none = True)) + except IOError, e: + return False + + return True class XendQCOWVDI(XendVDI): - vdi_type = "system" - def __init__(self, uuid, sr_uuid, qcow_path, image_path, vsize, psize): + def __init__(self, uuid, sr_uuid, qcow_path, image_path, cfg_path, + vsize, psize): XendVDI.__init__(self, uuid, sr_uuid) self.qcow_path = qcow_path self.image_path = image_path - self.vsize = vsize - self.psize = psize + self.cfg_path = cfg_path + self.physical_utilisation = psize + self.virtual_size = vsize + self.sector_size = 1 - def get_physical_utilisation(self): - return self.psize * KB - - def get_virtual_size(self): - return self.vsize * KB diff -r 58521d4b7c7b -r 91c7ee18c978 tools/python/scripts/xapi.vdicfg.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/scripts/xapi.vdicfg.py Fri Oct 13 15:13:21 2006 +0100 @@ -0,0 +1,7 @@ +name_label = 'VDI 1' +name_description = '' +virtual_size = 10 * 1024 +sector_size = 1024 +type = 'system' +sharable = False +read_only = False _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |