debuggers.hg
changeset 13624:665be23d7fe9
[XEND] Add support for multiple storage repositories.
Splits XendStorageRepository into a helper class and
XendQCowStorageRepo as the QCoW File backed repository. This gives us
the opportunity to introduce a pseudo storage repository to represent
the old way of specifying block devices. This is the
XendLocalStorageRepo.
Note that this still introduces a non-Xen API 'uri' attribute to VDIs
in order to support querying VDIs for their actual location relative
to the Xen host.
The QCoW backed repository is now named QCoW, and the pseudo-SR is
named Local.
This removes the 'image' attribute backwards-compat hack in VBDs in
the Xen API.
Signed-off-by: Alastair Tse <atse@xensource.com>
Splits XendStorageRepository into a helper class and
XendQCowStorageRepo as the QCoW File backed repository. This gives us
the opportunity to introduce a pseudo storage repository to represent
the old way of specifying block devices. This is the
XendLocalStorageRepo.
Note that this still introduces a non-Xen API 'uri' attribute to VDIs
in order to support querying VDIs for their actual location relative
to the Xen host.
The QCoW backed repository is now named QCoW, and the pseudo-SR is
named Local.
This removes the 'image' attribute backwards-compat hack in VBDs in
the Xen API.
Signed-off-by: Alastair Tse <atse@xensource.com>
author | Alastair Tse <atse@xensource.com> |
---|---|
date | Wed Jan 24 15:47:31 2007 +0000 (2007-01-24) |
parents | bea3d48576c6 |
children | 072ce22bcc91 |
files | tools/python/xen/xend/XendAPI.py tools/python/xen/xend/XendConfig.py tools/python/xen/xend/XendDomainInfo.py tools/python/xen/xend/XendLocalStorageRepo.py tools/python/xen/xend/XendNode.py tools/python/xen/xend/XendOptions.py tools/python/xen/xend/XendQCoWStorageRepo.py tools/python/xen/xend/XendStorageRepository.py tools/python/xen/xend/XendVDI.py |
line diff
1.1 --- a/tools/python/xen/xend/XendAPI.py Wed Jan 24 14:25:21 2007 +0000 1.2 +++ b/tools/python/xen/xend/XendAPI.py Wed Jan 24 15:47:31 2007 +0000 1.3 @@ -214,7 +214,7 @@ def valid_vdi(func): 1.4 @rtype: callable object 1.5 """ 1.6 return lambda *args, **kwargs: \ 1.7 - _check_ref(XendNode.instance().get_sr().is_valid_vdi, 1.8 + _check_ref(XendNode.instance().is_valid_vdi, 1.9 'VDI_HANDLE_INVALID', func, *args, **kwargs) 1.10 1.11 def valid_vtpm(func): 1.12 @@ -234,7 +234,7 @@ def valid_sr(func): 1.13 @rtype: callable object 1.14 """ 1.15 return lambda *args, **kwargs: \ 1.16 - _check_ref(lambda r: XendNode.instance().get_sr().uuid == r, 1.17 + _check_ref(lambda r: XendNode.instance().is_valid_sr, 1.18 'SR_HANDLE_INVALID', func, *args, **kwargs) 1.19 1.20 def valid_pif(func): 1.21 @@ -1203,8 +1203,7 @@ class XendAPI: 1.22 # Xen API: Class VBD 1.23 # ---------------------------------------------------------------- 1.24 1.25 - VBD_attr_ro = ['image', 1.26 - 'io_read_kbs', 1.27 + VBD_attr_ro = ['io_read_kbs', 1.28 'io_write_kbs'] 1.29 VBD_attr_rw = ['VM', 1.30 'VDI', 1.31 @@ -1213,7 +1212,7 @@ class XendAPI: 1.32 'type', 1.33 'driver'] 1.34 1.35 - VBD_attr_inst = VBD_attr_rw + ['image'] 1.36 + VBD_attr_inst = VBD_attr_rw 1.37 1.38 VBD_methods = [('media_change', None)] 1.39 VBD_funcs = [('create', 'VBD')] 1.40 @@ -1250,18 +1249,15 @@ class XendAPI: 1.41 dom = xendom.get_vm_by_uuid(vbd_struct['VM']) 1.42 vbd_ref = '' 1.43 try: 1.44 - if not vbd_struct.get('VDI', None): 1.45 - # this is a traditional VBD without VDI and SR 1.46 - vbd_ref = dom.create_vbd(vbd_struct) 1.47 - else: 1.48 - # new VBD via VDI/SR 1.49 - vdi_ref = vbd_struct.get('VDI') 1.50 - sr = XendNode.instance().get_sr() 1.51 - vdi_image = sr.xen_api_get_by_uuid(vdi_ref) 1.52 - if not vdi_image: 1.53 - return xen_api_error(['VDI_HANDLE_INVALID', vdi_ref]) 1.54 - vdi_image = vdi_image.qcow_path 1.55 - vbd_ref = dom.create_vbd_with_vdi(vbd_struct, vdi_image) 1.56 + # new VBD via VDI/SR 1.57 + vdi_ref = vbd_struct.get('VDI') 1.58 + vdi = XendNode.instance().get_vdi_by_uuid(vdi_ref) 1.59 + if not vdi: 1.60 + return xen_api_error(['VDI_HANDLE_INVALID', vdi_ref]) 1.61 + vdi_image = vdi.get_image_uri() 1.62 + vbd_ref = XendTask.log_progress(0, 100, 1.63 + dom.create_vbd, 1.64 + vbd_struct, vdi_image) 1.65 except XendError: 1.66 return xen_api_todo() 1.67 1.68 @@ -1275,7 +1271,7 @@ class XendAPI: 1.69 if not vm: 1.70 return xen_api_error(['VBD_HANDLE_INVALID', vbd_ref]) 1.71 1.72 - vm.destroy_vbd(vbd_ref) 1.73 + XendTask.log_progress(0, 100, vm.destroy_vbd, vbd_ref) 1.74 return xen_api_success_void() 1.75 1.76 # attributes (rw) 1.77 @@ -1446,7 +1442,7 @@ class XendAPI: 1.78 ('get_by_name_label', 'Set(VDI)')] 1.79 1.80 def _get_VDI(self, ref): 1.81 - return XendNode.instance().get_sr().xen_api_get_by_uuid(ref) 1.82 + return XendNode.instance().get_vdi_by_uuid(ref) 1.83 1.84 def VDI_get_VBDs(self, session, vdi_ref): 1.85 return xen_api_todo() 1.86 @@ -1474,8 +1470,7 @@ class XendAPI: 1.87 return xen_api_success(self._get_VDI(vdi_ref).name_description) 1.88 1.89 def VDI_get_SR(self, session, vdi_ref): 1.90 - sr = XendNode.instance().get_sr() 1.91 - return xen_api_success(sr.uuid) 1.92 + return xen_api_success(self._get_VDI(vdi_ref).sr_uuid) 1.93 1.94 def VDI_get_virtual_size(self, session, vdi_ref): 1.95 return xen_api_success(self._get_VDI(vdi_ref).virtual_size) 1.96 @@ -1513,18 +1508,17 @@ class XendAPI: 1.97 return xen_api_todo() 1.98 1.99 def VDI_destroy(self, session, vdi_ref): 1.100 - sr = XendNode.instance().get_sr() 1.101 - sr.destroy_image(vdi_ref) 1.102 + sr = XendNode.instance().get_sr_containing_vdi(vdi_ref) 1.103 + sr.destroy_vdi(vdi_ref) 1.104 return xen_api_success_void() 1.105 1.106 def VDI_get_record(self, session, vdi_ref): 1.107 - sr = XendNode.instance().get_sr() 1.108 - image = sr.xen_api_get_by_uuid(vdi_ref) 1.109 + image = XendNode.instance().get_vdi_by_uuid(vdi_ref) 1.110 return xen_api_success({ 1.111 'uuid': vdi_ref, 1.112 'name_label': image.name_label, 1.113 'name_description': image.name_description, 1.114 - 'SR': sr.uuid, 1.115 + 'SR': image.sr_uuid, 1.116 'VBDs': [], # TODO 1.117 'virtual_size': image.virtual_size, 1.118 'physical_utilisation': image.physical_utilisation, 1.119 @@ -1538,25 +1532,22 @@ class XendAPI: 1.120 1.121 # Class Functions 1.122 def VDI_create(self, session, vdi_struct): 1.123 - sr = XendNode.instance().get_sr() 1.124 sr_ref = vdi_struct.get('SR') 1.125 + xennode = XendNode.instance() 1.126 + if not xennode.is_valid_sr(sr_ref): 1.127 + return xen_api_error(['SR_HANDLE_INVALID', sr_ref]) 1.128 1.129 - if sr.uuid != sr_ref: 1.130 - return xen_api_error(['SR_HANDLE_INVALID', vdi_struct.get('SR')]) 1.131 - 1.132 - vdi_uuid = sr.create_image(vdi_struct) 1.133 + vdi_uuid = xennode.srs[sr_ref].create_vdi(vdi_struct) 1.134 return xen_api_success(vdi_uuid) 1.135 1.136 def VDI_get_all(self, session): 1.137 - sr = XendNode.instance().get_sr() 1.138 - return xen_api_success(sr.list_images()) 1.139 + xennode = XendNode.instance() 1.140 + vdis = [sr.get_vdis() for sr in xennode.srs.values()] 1.141 + return xen_api_success(reduce(lambda x, y: x + y, vdis)) 1.142 1.143 def VDI_get_by_name_label(self, session, name): 1.144 - sr = XendNode.instance().get_sr() 1.145 - image_uuid = sr.xen_api_get_by_name_label(name) 1.146 - if image_uuid: 1.147 - return xen_api_success([image_uuid]) 1.148 - return xen_api_success([]) 1.149 + xennode = XendNode.instance() 1.150 + return xen_api_success(xennode.get_vdi_by_name_label(name)) 1.151 1.152 1.153 # Xen API: Class VTPM 1.154 @@ -1691,15 +1682,11 @@ class XendAPI: 1.155 1.156 # Class Functions 1.157 def SR_get_all(self, session): 1.158 - sr = XendNode.instance().get_sr() 1.159 - return xen_api_success([sr.uuid]) 1.160 - 1.161 + return xen_api_success(XendNode.instance().get_all_sr_uuid()) 1.162 + 1.163 def SR_get_by_name_label(self, session, label): 1.164 - sr = XendNode.instance().get_sr() 1.165 - if sr.name_label != label: 1.166 - return xen_api_success([]) 1.167 - return xen_api_success([sr.uuid]) 1.168 - 1.169 + return xen_api_success(XendNode.instance().get_sr_by_name(label)) 1.170 + 1.171 def SR_create(self, session): 1.172 return xen_api_error(XEND_ERROR_UNSUPPORTED) 1.173 1.174 @@ -1711,16 +1698,20 @@ class XendAPI: 1.175 return xen_api_error(XEND_ERROR_UNSUPPORTED) 1.176 1.177 def SR_get_record(self, session, sr_ref): 1.178 - sr = XendNode.instance().get_sr() 1.179 - return xen_api_success(sr.get_record()) 1.180 + sr = XendNode.instance().get_sr(sr_ref) 1.181 + if sr: 1.182 + return xen_api_success(sr.get_record()) 1.183 + return xen_api_error(['SR_HANDLE_INVALID', sr_ref]) 1.184 1.185 # Attribute acceess 1.186 1.187 - def _get_SR_func(self, _, func): 1.188 - return xen_api_success(getattr(XendNode.instance().get_sr(), func)()) 1.189 + def _get_SR_func(self, sr_ref, func): 1.190 + return xen_api_success(getattr(XendNode.instance().get_sr(sr_ref), 1.191 + func)()) 1.192 1.193 - def _get_SR_attr(self, _, attr): 1.194 - return xen_api_success(getattr(XendNode.instance().get_sr(), attr)) 1.195 + def _get_SR_attr(self, sr_ref, attr): 1.196 + return xen_api_success(getattr(XendNode.instance().get_sr(sr_ref), 1.197 + attr)) 1.198 1.199 def SR_get_VDIs(self, _, ref): 1.200 return self._get_SR_func(ref, 'list_images') 1.201 @@ -1729,10 +1720,10 @@ class XendAPI: 1.202 return self._get_SR_func(ref, 'virtual_allocation') 1.203 1.204 def SR_get_physical_utilisation(self, _, ref): 1.205 - return self._get_SR_func(ref, 'used_space_bytes') 1.206 + return self._get_SR_func(ref, 'physical_utilisation') 1.207 1.208 def SR_get_physical_size(self, _, ref): 1.209 - return self._get_SR_func(ref, 'total_space_bytes') 1.210 + return self._get_SR_func(ref, 'physical_size') 1.211 1.212 def SR_get_type(self, _, ref): 1.213 return self._get_SR_attr(ref, 'type') 1.214 @@ -1747,15 +1738,17 @@ class XendAPI: 1.215 return self._get_SR_attr(ref, 'name_description') 1.216 1.217 def SR_set_name_label(self, session, sr_ref, value): 1.218 - sr = XendNode.instance().get_sr() 1.219 - sr.name_label = value 1.220 - XendNode.instance().save() 1.221 + sr = XendNode.instance.get_sr(sr_ref) 1.222 + if sr: 1.223 + sr.name_label = value 1.224 + XendNode.instance().save() 1.225 return xen_api_success_void() 1.226 1.227 def SR_set_name_description(self, session, sr_ref, value): 1.228 - sr = XendNode.instance().get_sr() 1.229 - sr.name_description = value 1.230 - XendNode.instance().save() 1.231 + sr = XendNode.instance.get_sr(sr_ref) 1.232 + if sr: 1.233 + sr.name_description = value 1.234 + XendNode.instance().save() 1.235 return xen_api_success_void() 1.236 1.237
2.1 --- a/tools/python/xen/xend/XendConfig.py Wed Jan 24 14:25:21 2007 +0000 2.2 +++ b/tools/python/xen/xend/XendConfig.py Wed Jan 24 15:47:31 2007 +0000 2.3 @@ -994,13 +994,15 @@ class XendConfig(dict): 2.4 return dev_uuid 2.5 2.6 elif dev_type in ('vbd', 'tap'): 2.7 - if dev_type == 'vbd': 2.8 - dev_info['uname'] = cfg_xenapi.get('image', '') 2.9 - dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device') 2.10 - elif dev_type == 'tap': 2.11 - dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image') 2.12 - dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device') 2.13 + dev_info['type'] = cfg_xenapi.get('type', 'Disk') 2.14 + if dev_info['type'] == 'CD': 2.15 + old_vbd_type = 'cdrom' 2.16 + else: 2.17 + old_vbd_type = 'disk' 2.18 2.19 + dev_info['uname'] = cfg_xenapi.get('image', '') 2.20 + dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'), 2.21 + old_vbd_type) 2.22 dev_info['driver'] = cfg_xenapi.get('driver') 2.23 dev_info['VDI'] = cfg_xenapi.get('VDI', '') 2.24
3.1 --- a/tools/python/xen/xend/XendDomainInfo.py Wed Jan 24 14:25:21 2007 +0000 3.2 +++ b/tools/python/xen/xend/XendDomainInfo.py Wed Jan 24 15:47:31 2007 +0000 3.3 @@ -1656,7 +1656,7 @@ class XendDomainInfo: 3.4 3.5 from xen.xend import XendDomain 3.6 dom0 = XendDomain.instance().privilegedDomain() 3.7 - dom0._waitForDeviceUUID(dom0.create_vbd_with_vdi(vbd, fn)) 3.8 + dom0._waitForDeviceUUID(dom0.create_vbd(vbd, fn)) 3.9 fn = BOOTLOADER_LOOPBACK_DEVICE 3.10 3.11 try: 3.12 @@ -2133,24 +2133,7 @@ class XendDomainInfo: 3.13 def get_vtpms(self): 3.14 return self.info.get('vtpm_refs', []) 3.15 3.16 - def create_vbd(self, xenapi_vbd): 3.17 - """Create a VBD device from the passed struct in Xen API format. 3.18 - 3.19 - @return: uuid of the device 3.20 - @rtype: string 3.21 - """ 3.22 - 3.23 - dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd) 3.24 - if not dev_uuid: 3.25 - raise XendError('Failed to create device') 3.26 - 3.27 - if self.state == XEN_API_VM_POWER_STATE_RUNNING: 3.28 - _, config = self.info['devices'][dev_uuid] 3.29 - config['devid'] = self.getDeviceController('vbd').createDevice(config) 3.30 - 3.31 - return dev_uuid 3.32 - 3.33 - def create_vbd_with_vdi(self, xenapi_vbd, vdi_image_path): 3.34 + def create_vbd(self, xenapi_vbd, vdi_image_path): 3.35 """Create a VBD using a VDI from XendStorageRepository. 3.36 3.37 @param xenapi_vbd: vbd struct from the Xen API 3.38 @@ -2159,14 +2142,26 @@ class XendDomainInfo: 3.39 @return: uuid of the device 3.40 """ 3.41 xenapi_vbd['image'] = vdi_image_path 3.42 - log.debug('create_vbd_with_vdi: %s' % xenapi_vbd) 3.43 - dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd) 3.44 + log.debug('create_vbd: %s' % xenapi_vbd) 3.45 + dev_uuid = '' 3.46 + if vdi_image_path.startswith('tap'): 3.47 + dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd) 3.48 + else: 3.49 + dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd) 3.50 + 3.51 if not dev_uuid: 3.52 raise XendError('Failed to create device') 3.53 3.54 if self.state == XEN_API_VM_POWER_STATE_RUNNING: 3.55 _, config = self.info['devices'][dev_uuid] 3.56 - config['devid'] = self.getDeviceController('tap').createDevice(config) 3.57 + dev_control = None 3.58 + 3.59 + if vdi_image_path.startswith('tap'): 3.60 + dev_control = self.getDeviceController('tap') 3.61 + else: 3.62 + dev_control = self.getDeviceController('vbd') 3.63 + 3.64 + config['devid'] = dev_control.createDevice(config) 3.65 3.66 return dev_uuid 3.67
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/tools/python/xen/xend/XendLocalStorageRepo.py Wed Jan 24 15:47:31 2007 +0000 4.3 @@ -0,0 +1,94 @@ 4.4 +#!/usr/bin/python 4.5 +#============================================================================ 4.6 +# This library is free software; you can redistribute it and/or 4.7 +# modify it under the terms of version 2.1 of the GNU Lesser General Public 4.8 +# License as published by the Free Software Foundation. 4.9 +# 4.10 +# This library is distributed in the hope that it will be useful, 4.11 +# but WITHOUT ANY WARRANTY; without even the implied warranty of 4.12 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 4.13 +# Lesser General Public License for more details. 4.14 +# 4.15 +# You should have received a copy of the GNU Lesser General Public 4.16 +# License along with this library; if not, write to the Free Software 4.17 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 4.18 +#============================================================================ 4.19 +# Copyright (C) 2007 XenSource Ltd. 4.20 +#============================================================================ 4.21 +# 4.22 +# A pseudo-StorageRepository to provide a representation for the images 4.23 +# that can be specified by xm. 4.24 +# 4.25 + 4.26 +import commands 4.27 +import logging 4.28 +import os 4.29 +import stat 4.30 +import threading 4.31 +import re 4.32 +import sys 4.33 +import struct 4.34 + 4.35 +from xen.util import mkdir 4.36 +from xen.xend import uuid 4.37 +from xen.xend.XendError import XendError 4.38 +from xen.xend.XendVDI import * 4.39 +from xen.xend.XendTask import XendTask 4.40 +from xen.xend.XendStorageRepository import XendStorageRepository 4.41 +from xen.xend.XendStateStore import XendStateStore 4.42 +from xen.xend.XendOptions import instance as xendoptions 4.43 + 4.44 +MB = 1024 * 1024 4.45 + 4.46 +log = logging.getLogger("xend.XendLocalStorageRepo") 4.47 + 4.48 +class XendLocalStorageRepo(XendStorageRepository): 4.49 + """A backwards compatibility storage repository so that 4.50 + traditional file:/dir/file.img and phy:/dev/hdxx images can 4.51 + still be represented in terms of the Xen API. 4.52 + """ 4.53 + 4.54 + def __init__(self, sr_uuid, sr_type = 'local', 4.55 + name_label = 'Local', 4.56 + name_description = 'Traditional Local Storage Repo'): 4.57 + """ 4.58 + @ivar images: mapping of all the images. 4.59 + @type images: dictionary by image uuid. 4.60 + @ivar lock: lock to provide thread safety. 4.61 + """ 4.62 + 4.63 + XendStorageRepository.__init__(self, sr_uuid, sr_type, 4.64 + name_label, name_description, 4.65 + '/') 4.66 + 4.67 + self.state = XendStateStore(xendoptions().get_xend_state_path() 4.68 + + '/local_sr') 4.69 + 4.70 + stored_images = self.state.load_state('vdi') 4.71 + if stored_images: 4.72 + for image_uuid, image in stored_images.items(): 4.73 + self.images[image_uuid] = XendLocalVDI(image) 4.74 + 4.75 + def create_vdi(self, vdi_struct): 4.76 + """ Creates a fake VDI image for a traditional image string. 4.77 + 4.78 + The image uri is stored in the attribute 'uri' 4.79 + """ 4.80 + vdi_struct['uuid'] = uuid.createString() 4.81 + vdi_struct['SR'] = self.uuid 4.82 + new_image = XendLocalVDI(vdi_struct) 4.83 + self.images[new_image.uuid] = new_image 4.84 + self.save_state() 4.85 + return new_image.uuid 4.86 + 4.87 + def save_state(self): 4.88 + vdi_records = dict([(k, v.get_record(transient = False)) 4.89 + for k, v in self.images.items()]) 4.90 + self.state.save_state('vdi', vdi_records) 4.91 + 4.92 + def destroy_vdi(self, vdi_uuid): 4.93 + if vdi_uuid in self.images: 4.94 + del self.images[vdi_uuid] 4.95 + 4.96 + self.save_state() 4.97 +
5.1 --- a/tools/python/xen/xend/XendNode.py Wed Jan 24 14:25:21 2007 +0000 5.2 +++ b/tools/python/xen/xend/XendNode.py Wed Jan 24 15:47:31 2007 +0000 5.3 @@ -25,7 +25,8 @@ from xen.util import Brctl 5.4 from xen.xend import uuid 5.5 from xen.xend.XendError import XendError, NetworkAlreadyConnected 5.6 from xen.xend.XendOptions import instance as xendoptions 5.7 -from xen.xend.XendStorageRepository import XendStorageRepository 5.8 +from xen.xend.XendQCoWStorageRepo import XendQCoWStorageRepo 5.9 +from xen.xend.XendLocalStorageRepo import XendLocalStorageRepo 5.10 from xen.xend.XendLogging import log 5.11 from xen.xend.XendPIF import * 5.12 from xen.xend.XendNetwork import * 5.13 @@ -88,7 +89,8 @@ class XendNode: 5.14 5.15 self.pifs = {} 5.16 self.networks = {} 5.17 - 5.18 + self.srs = {} 5.19 + 5.20 # initialise networks 5.21 saved_networks = self.state_store.load_state('network') 5.22 if saved_networks: 5.23 @@ -121,13 +123,23 @@ class XendNode: 5.24 self.PIF_create(name, mtu, '', mac, network, False) 5.25 5.26 # initialise storage 5.27 - saved_sr = self.state_store.load_state('sr') 5.28 - if saved_sr and len(saved_sr) == 1: 5.29 - sr_uuid = saved_sr.keys()[0] 5.30 - self.sr = XendStorageRepository(sr_uuid) 5.31 - else: 5.32 - sr_uuid = uuid.createString() 5.33 - self.sr = XendStorageRepository(sr_uuid) 5.34 + saved_srs = self.state_store.load_state('sr') 5.35 + if saved_srs: 5.36 + for sr_uuid, sr_cfg in saved_srs.items(): 5.37 + if sr_cfg['type'] == 'qcow_file': 5.38 + self.srs[sr_uuid] = XendQCoWStorageRepo(sr_uuid) 5.39 + elif sr_cfg['type'] == 'local_image': 5.40 + self.srs[sr_uuid] = XendLocalStorageRepo(sr_uuid) 5.41 + 5.42 + # Create missing SRs if they don't exist 5.43 + if not self.get_sr_by_type('local_image'): 5.44 + image_sr_uuid = uuid.createString() 5.45 + self.srs[image_sr_uuid] = XendLocalStorageRepo(image_sr_uuid) 5.46 + 5.47 + if not self.get_sr_by_type('qcow_file'): 5.48 + qcow_sr_uuid = uuid.createString() 5.49 + self.srs[qcow_sr_uuid] = XendQCowStorageRepo(qcow_sr_uuid) 5.50 + 5.51 5.52 5.53 def network_create(self, name_label, name_description, 5.54 @@ -184,9 +196,7 @@ class XendNode: 5.55 self.state_store.save_state('cpu', self.cpus) 5.56 self.save_PIFs() 5.57 self.save_networks() 5.58 - 5.59 - sr_record = {self.sr.uuid: self.sr.get_record()} 5.60 - self.state_store.save_state('sr', sr_record) 5.61 + self.save_SRs() 5.62 5.63 def save_PIFs(self): 5.64 pif_records = dict([(k, v.get_record(transient = False)) 5.65 @@ -198,6 +208,11 @@ class XendNode: 5.66 for k, v in self.networks.items()]) 5.67 self.state_store.save_state('network', net_records) 5.68 5.69 + def save_SRs(self): 5.70 + sr_records = dict([(k, v.get_record(transient = False)) 5.71 + for k, v in self.srs.items()]) 5.72 + self.state_store.save_state('sr', sr_records) 5.73 + 5.74 def shutdown(self): 5.75 return 0 5.76 5.77 @@ -206,7 +221,6 @@ class XendNode: 5.78 5.79 def notify(self, _): 5.80 return 0 5.81 - 5.82 5.83 # 5.84 # Ref validation 5.85 @@ -221,12 +235,50 @@ class XendNode: 5.86 def is_valid_network(self, network_ref): 5.87 return (network_ref in self.networks) 5.88 5.89 + def is_valid_sr(self, sr_ref): 5.90 + return (sr_ref in self.srs) 5.91 + 5.92 + def is_valid_vdi(self, vdi_ref): 5.93 + for sr in self.srs.values(): 5.94 + if sr.is_valid_vdi(vdi_ref): 5.95 + return True 5.96 + return False 5.97 + 5.98 # 5.99 - # Storage Repo 5.100 + # Storage Repositories 5.101 # 5.102 5.103 - def get_sr(self): 5.104 - return self.sr 5.105 + def get_sr(self, sr_uuid): 5.106 + return self.srs.get(sr_uuid) 5.107 + 5.108 + def get_sr_by_type(self, sr_type): 5.109 + return [sr.uuid for sr in self.srs.values() if sr.type == sr_type] 5.110 + 5.111 + def get_sr_by_name(self, name): 5.112 + return [sr.uuid for sr in self.srs.values() if sr.name_label == name] 5.113 + 5.114 + def get_all_sr_uuid(self): 5.115 + return self.srs.keys() 5.116 + 5.117 + def get_vdi_by_uuid(self, vdi_uuid): 5.118 + for sr in self.srs.values(): 5.119 + if sr.is_valid_vdi(vdi_uuid): 5.120 + return sr.get_vdi_by_uuid(vdi_uuid) 5.121 + return None 5.122 + 5.123 + def get_vdi_by_name_label(self, name): 5.124 + for sr in self.srs.values(): 5.125 + vdi = sr.get_vdi_by_name_label(name) 5.126 + if vdi: 5.127 + return vdi 5.128 + return None 5.129 + 5.130 + def get_sr_containing_vdi(self, vdi_uuid): 5.131 + for sr in self.srs.values(): 5.132 + if sr.is_valid_vdi(vdi_uuid): 5.133 + return sr 5.134 + return None 5.135 + 5.136 5.137 # 5.138 # Host Functions
6.1 --- a/tools/python/xen/xend/XendOptions.py Wed Jan 24 14:25:21 2007 +0000 6.2 +++ b/tools/python/xen/xend/XendOptions.py Wed Jan 24 15:47:31 2007 +0000 6.3 @@ -104,6 +104,9 @@ class XendOptions: 6.4 """Default xend management state storage.""" 6.5 xend_state_path_default = '/var/lib/xend/state' 6.6 6.7 + """Default xend QCoW storage repository location.""" 6.8 + xend_storage_path_default = '/var/lib/xend/storage' 6.9 + 6.10 """Default type of backend network interfaces""" 6.11 netback_type = osdep.netback_type 6.12 6.13 @@ -211,7 +214,12 @@ class XendOptions: 6.14 def get_xend_state_path(self): 6.15 """ Get the path for persistent domain configuration storage 6.16 """ 6.17 - return self.get_config_string("xend-state-path", self.xend_state_path_default) 6.18 + return self.get_config_string("xend-state-path", self.xend_state_path_default) 6.19 + 6.20 + def get_xend_storage_path(self): 6.21 + """ Get the path for persistent domain configuration storage 6.22 + """ 6.23 + return self.get_config_string("xend-storage-path", self.xend_storage_path_default) 6.24 6.25 def get_network_script(self): 6.26 """@return the script used to alter the network configuration when
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/tools/python/xen/xend/XendQCoWStorageRepo.py Wed Jan 24 15:47:31 2007 +0000 7.3 @@ -0,0 +1,366 @@ 7.4 +#!/usr/bin/python 7.5 +#============================================================================ 7.6 +# This library is free software; you can redistribute it and/or 7.7 +# modify it under the terms of version 2.1 of the GNU Lesser General Public 7.8 +# License as published by the Free Software Foundation. 7.9 +# 7.10 +# This library is distributed in the hope that it will be useful, 7.11 +# but WITHOUT ANY WARRANTY; without even the implied warranty of 7.12 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 7.13 +# Lesser General Public License for more details. 7.14 +# 7.15 +# You should have received a copy of the GNU Lesser General Public 7.16 +# License along with this library; if not, write to the Free Software 7.17 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 7.18 +#============================================================================ 7.19 +# Copyright (C) 2006,2007 XenSource Ltd. 7.20 +#============================================================================ 7.21 +# 7.22 +# The default QCOW Xen API Storage Repository 7.23 +# 7.24 + 7.25 +import commands 7.26 +import logging 7.27 +import os 7.28 +import stat 7.29 +import threading 7.30 +import re 7.31 +import sys 7.32 +import struct 7.33 + 7.34 +from xen.util import mkdir 7.35 +from xen.xend import uuid 7.36 +from xen.xend.XendError import XendError 7.37 +from xen.xend.XendVDI import * 7.38 +from xen.xend.XendTask import XendTask 7.39 +from xen.xend.XendStorageRepository import XendStorageRepository 7.40 +from xen.xend.XendOptions import instance as xendoptions 7.41 + 7.42 +XEND_STORAGE_NO_MAXIMUM = sys.maxint 7.43 +XEND_STORAGE_QCOW_FILENAME = "%s.qcow" 7.44 +XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml" 7.45 +QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create -r %d %s" 7.46 + 7.47 +MB = 1024 * 1024 7.48 + 7.49 +log = logging.getLogger("xend.XendQCowStorageRepo") 7.50 + 7.51 + 7.52 +def qcow_virtual_size(qcow_file): 7.53 + """Read the first 32 bytes of the QCoW header to determine its size. 7.54 + 7.55 + See: http://www.gnome.org/~markmc/qcow-image-format.html. 7.56 + """ 7.57 + try: 7.58 + qcow_header = open(qcow_file, 'rb').read(32) 7.59 + parts = struct.unpack('>IIQIIQ', qcow_header) 7.60 + return parts[-1] 7.61 + except IOError: 7.62 + return -1 7.63 + 7.64 +class XendQCoWStorageRepo(XendStorageRepository): 7.65 + """A simple file backed QCOW Storage Repository. 7.66 + 7.67 + This class exposes the interface to create VDI's via the 7.68 + Xen API. The backend is a file-backed QCOW format that is stored 7.69 + in XEND_STORAGE_DIR or any that is specified in the constructor. 7.70 + 7.71 + The actual images are created in the format <uuid>.img and <uuid>.qcow. 7.72 + """ 7.73 + 7.74 + def __init__(self, sr_uuid, 7.75 + sr_type = "qcow_file", 7.76 + name_label = "QCoW", 7.77 + name_description = "Xend QCoW Storage Repository", 7.78 + location = xendoptions().get_xend_storage_path(), 7.79 + storage_max = XEND_STORAGE_NO_MAXIMUM): 7.80 + """ 7.81 + @keyword storage_max: Maximum disk space to use in bytes. 7.82 + @type storage_max: int 7.83 + 7.84 + @ivar storage_free: storage space free for this repository 7.85 + @ivar images: mapping of all the images. 7.86 + @type images: dictionary by image uuid. 7.87 + @ivar lock: lock to provide thread safety. 7.88 + """ 7.89 + 7.90 + XendStorageRepository.__init__(self, sr_uuid, sr_type, name_label, 7.91 + name_description, location, 7.92 + storage_max) 7.93 + self.storage_free = 0 7.94 + self._refresh() 7.95 + 7.96 + def get_record(self, transient = True): 7.97 + retval = {'uuid': self.uuid, 7.98 + 'name_label': self.name_label, 7.99 + 'name_description': self.name_description, 7.100 + 'virtual_allocation': self.virtual_allocation, 7.101 + 'physical_utilisation': self.physical_utilisation, 7.102 + 'physical_size': self.physical_size, 7.103 + 'type': self.type, 7.104 + 'location': self.location, 7.105 + 'VDIs': self.images.keys()} 7.106 + 7.107 + if self.physical_size == XEND_STORAGE_NO_MAXIMUM: 7.108 + stfs = os.statvfs(self.location) 7.109 + retval['physical_size'] = stfs.f_blocks * stfs.f_frsize 7.110 + 7.111 + return retval 7.112 + 7.113 + def _refresh(self): 7.114 + """Internal function that refreshes the state of the disk and 7.115 + updates the list of images available. 7.116 + """ 7.117 + self.lock.acquire() 7.118 + try: 7.119 + mkdir.parents(self.location, stat.S_IRWXU) 7.120 + 7.121 + # scan the directory and populate self.images 7.122 + virtual_alloc = 0 7.123 + physical_used = 0 7.124 + seen_images = [] 7.125 + for filename in os.listdir(self.location): 7.126 + if filename[-5:] == XEND_STORAGE_QCOW_FILENAME[-5:]: 7.127 + image_uuid = filename[:-5] 7.128 + seen_images.append(image_uuid) 7.129 + 7.130 + qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid 7.131 + cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid 7.132 + qcow_path = os.path.join(self.location, qcow_file) 7.133 + cfg_path = os.path.join(self.location, cfg_file) 7.134 + 7.135 + phys_size = os.stat(qcow_path).st_size 7.136 + virt_size = qcow_virtual_size(qcow_path) 7.137 + 7.138 + # add this image if we haven't seen it before 7.139 + if image_uuid not in self.images: 7.140 + vdi = XendQCoWVDI(image_uuid, self.uuid, 7.141 + qcow_path, cfg_path, 7.142 + virt_size, phys_size) 7.143 + 7.144 + if cfg_path and os.path.exists(cfg_path): 7.145 + try: 7.146 + vdi.load_config(cfg_path) 7.147 + except: 7.148 + log.error('Corrupt VDI configuration file %s' % 7.149 + cfg_path) 7.150 + 7.151 + self.images[image_uuid] = vdi 7.152 + 7.153 + physical_used += phys_size 7.154 + virtual_alloc += virt_size 7.155 + 7.156 + # remove images that aren't valid 7.157 + for image_uuid in self.images.keys(): 7.158 + if image_uuid not in seen_images: 7.159 + try: 7.160 + os.unlink(self.images[image_uuid].qcow_path) 7.161 + except OSError: 7.162 + pass 7.163 + del self.images[image_uuid] 7.164 + 7.165 + self.virtual_allocation = virtual_alloc 7.166 + self.physical_utilisation = physical_used 7.167 + 7.168 + # update free storage if we have to track that 7.169 + if self.physical_size == XEND_STORAGE_NO_MAXIMUM: 7.170 + self.storage_free = self._get_free_space() 7.171 + else: 7.172 + self.storage_free = self.physical_size - self.virtual_allocation 7.173 + 7.174 + finally: 7.175 + self.lock.release() 7.176 + 7.177 + def _get_free_space(self): 7.178 + """Returns the amount of free space in bytes available in the storage 7.179 + partition. Note that this may not be used if the storage repository 7.180 + is initialised with a maximum size in storage_max. 7.181 + 7.182 + @rtype: int 7.183 + """ 7.184 + stfs = os.statvfs(self.location) 7.185 + return stfs.f_bavail * stfs.f_frsize 7.186 + 7.187 + def _has_space_available_for(self, size_bytes): 7.188 + """Returns whether there is enough space for an image in the 7.189 + partition which the storage_dir resides on. 7.190 + 7.191 + @rtype: bool 7.192 + """ 7.193 + if self.physical_size != XEND_STORAGE_NO_MAXIMUM: 7.194 + return self.storage_free > size_bytes 7.195 + 7.196 + bytes_free = self._get_free_space() 7.197 + if size_bytes < bytes_free: 7.198 + return True 7.199 + return False 7.200 + 7.201 + def _create_image_files(self, desired_size_bytes): 7.202 + """Create an image and return its assigned UUID. 7.203 + 7.204 + @param desired_size_bytes: Desired image size in bytes 7.205 + @type desired_size_bytes: int 7.206 + @rtype: string 7.207 + @return: uuid 7.208 + 7.209 + @raises XendError: If an error occurs. 7.210 + """ 7.211 + self.lock.acquire() 7.212 + try: 7.213 + if not self._has_space_available_for(desired_size_bytes): 7.214 + raise XendError("Not enough space") 7.215 + 7.216 + image_uuid = uuid.createString() 7.217 + qcow_path = os.path.join(self.location, 7.218 + XEND_STORAGE_QCOW_FILENAME % image_uuid) 7.219 + 7.220 + if qcow_path and os.path.exists(qcow_path): 7.221 + raise XendError("Image with same UUID alreaady exists:" % 7.222 + image_uuid) 7.223 + 7.224 + cmd = QCOW_CREATE_COMMAND % (desired_size_bytes/MB, qcow_path) 7.225 + rc, output = commands.getstatusoutput(cmd) 7.226 + 7.227 + if rc != 0: 7.228 + # cleanup the image file 7.229 + os.unlink(qcow_path) 7.230 + raise XendError("Failed to create QCOW Image: %s" % output) 7.231 + 7.232 + self._refresh() 7.233 + return image_uuid 7.234 + finally: 7.235 + self.lock.release() 7.236 + 7.237 + def destroy_vdi(self, image_uuid): 7.238 + """Destroy an image that is managed by this storage repository. 7.239 + 7.240 + @param image_uuid: Image UUID 7.241 + @type image_uuid: String 7.242 + @rtype: String 7.243 + """ 7.244 + self.lock.acquire() 7.245 + try: 7.246 + if image_uuid in self.images: 7.247 + # TODO: check if it is being used? 7.248 + qcow_path = self.images[image_uuid].qcow_path 7.249 + cfg_path = self.images[image_uuid].cfg_path 7.250 + try: 7.251 + os.unlink(qcow_path) 7.252 + if cfg_path and os.path.exists(cfg_path): 7.253 + os.unlink(cfg_path) 7.254 + except OSError: 7.255 + log.exception("Failed to destroy image") 7.256 + del self.images[image_uuid] 7.257 + return True 7.258 + finally: 7.259 + self.lock.release() 7.260 + 7.261 + return False 7.262 + 7.263 + def list_images(self): 7.264 + """ List all the available images by UUID. 7.265 + 7.266 + @rtype: list of strings. 7.267 + @return: list of UUIDs 7.268 + """ 7.269 + self.lock.acquire() 7.270 + try: 7.271 + return self.images.keys() 7.272 + finally: 7.273 + self.lock.release() 7.274 + 7.275 + def free_space_bytes(self): 7.276 + """Returns the amount of available space in KB. 7.277 + @rtype: int 7.278 + """ 7.279 + self.lock.acquire() 7.280 + try: 7.281 + return self.storage_free 7.282 + finally: 7.283 + self.lock.release() 7.284 + 7.285 + def total_space_bytes(self): 7.286 + """Returns the total usable space of the storage repo in KB. 7.287 + @rtype: int 7.288 + """ 7.289 + self.lock.acquire() 7.290 + try: 7.291 + if self.physical_size == XEND_STORAGE_NO_MAXIMUM: 7.292 + stfs = os.statvfs(self.location) 7.293 + return stfs.f_blocks * stfs.f_frsize 7.294 + else: 7.295 + return self.physical_size 7.296 + finally: 7.297 + self.lock.release() 7.298 + 7.299 + def used_space_bytes(self): 7.300 + """Returns the total amount of space used by this storage repository. 7.301 + @rtype: int 7.302 + """ 7.303 + self.lock.acquire() 7.304 + try: 7.305 + return self.physical_utilisation 7.306 + finally: 7.307 + self.lock.release() 7.308 + 7.309 + def virtual_allocation(self): 7.310 + """Returns the total virtual space allocated within the storage repo. 7.311 + @rtype: int 7.312 + """ 7.313 + self.lock.acquire() 7.314 + try: 7.315 + return self.virtual_allocation 7.316 + finally: 7.317 + self.lock.release() 7.318 + 7.319 + 7.320 + def create_vdi(self, vdi_struct): 7.321 + image_uuid = None 7.322 + try: 7.323 + sector_count = int(vdi_struct.get('virtual_size', 0)) 7.324 + sector_size = int(vdi_struct.get('sector_size', 1024)) 7.325 + size_bytes = (sector_count * sector_size) 7.326 + 7.327 + image_uuid = self._create_image_files(size_bytes) 7.328 + 7.329 + image = self.images[image_uuid] 7.330 + image_cfg = { 7.331 + 'sector_size': sector_size, 7.332 + 'virtual_size': sector_count, 7.333 + 'type': vdi_struct.get('type', 'system'), 7.334 + 'name_label': vdi_struct.get('name_label', ''), 7.335 + 'name_description': vdi_struct.get('name_description', ''), 7.336 + 'sharable': bool(vdi_struct.get('sharable', False)), 7.337 + 'read_only': bool(vdi_struct.get('read_only', False)), 7.338 + } 7.339 + 7.340 + # load in configuration from vdi_struct 7.341 + image.load_config_dict(image_cfg) 7.342 + 7.343 + # save configuration to file 7.344 + cfg_filename = XEND_STORAGE_VDICFG_FILENAME % image_uuid 7.345 + cfg_path = os.path.join(self.location, cfg_filename) 7.346 + image.save_config(cfg_path) 7.347 + 7.348 + except Exception, e: 7.349 + # cleanup before raising exception 7.350 + if image_uuid: 7.351 + self.destroy_vdi(image_uuid) 7.352 + 7.353 + raise 7.354 + 7.355 + return image_uuid 7.356 + 7.357 + 7.358 +# remove everything below this line!! for testing only 7.359 +if __name__ == "__main__": 7.360 + xsr = XendStorageRepository() 7.361 + print 'Free Space: %d MB' % (xsr.free_space_bytes()/MB) 7.362 + print "Create Image:", 7.363 + print xsr._create_image_files(10 * MB) 7.364 + print 'Delete all images:' 7.365 + for image_uuid in xsr.list_images(): 7.366 + print image_uuid, 7.367 + xsr._destroy_image_files(image_uuid) 7.368 + 7.369 + print
8.1 --- a/tools/python/xen/xend/XendStorageRepository.py Wed Jan 24 14:25:21 2007 +0000 8.2 +++ b/tools/python/xen/xend/XendStorageRepository.py Wed Jan 24 15:47:31 2007 +0000 8.3 @@ -13,65 +13,28 @@ 8.4 # License along with this library; if not, write to the Free Software 8.5 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 8.6 #============================================================================ 8.7 -# Copyright (C) 2006 XenSource Ltd. 8.8 +# Copyright (C) 2006, 2007 XenSource Ltd. 8.9 #============================================================================ 8.10 # 8.11 -# The default QCOW Xen API Storage Repository 8.12 +# Abstract class for XendStorageRepositories 8.13 # 8.14 8.15 -import commands 8.16 -import logging 8.17 -import os 8.18 -import stat 8.19 import threading 8.20 -import re 8.21 import sys 8.22 -import struct 8.23 8.24 -from xen.util import mkdir 8.25 -from xen.xend import uuid 8.26 from xen.xend.XendError import XendError 8.27 from xen.xend.XendVDI import * 8.28 8.29 - 8.30 XEND_STORAGE_NO_MAXIMUM = sys.maxint 8.31 -XEND_STORAGE_DIR = "/var/lib/xend/storage/" 8.32 -XEND_STORAGE_QCOW_FILENAME = "%s.qcow" 8.33 -XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml" 8.34 -QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create -r %d %s" 8.35 - 8.36 -MB = 1024 * 1024 8.37 - 8.38 -log = logging.getLogger("xend.XendStorageRepository") 8.39 - 8.40 - 8.41 -def qcow_virtual_size(qcow_file): 8.42 - """Read the first 32 bytes of the QCoW header to determine its size. 8.43 - 8.44 - See: http://www.gnome.org/~markmc/qcow-image-format.html. 8.45 - """ 8.46 - try: 8.47 - qcow_header = open(qcow_file, 'rb').read(32) 8.48 - parts = struct.unpack('>IIQIIQ', qcow_header) 8.49 - return parts[-1] 8.50 - except IOError: 8.51 - return -1 8.52 8.53 class XendStorageRepository: 8.54 - """A simple file backed QCOW Storage Repository. 8.55 - 8.56 - This class exposes the interface to create VDI's via the 8.57 - Xen API. The backend is a file-backed QCOW format that is stored 8.58 - in XEND_STORAGE_DIR or any that is specified in the constructor. 8.59 + """ Base class for Storage Repos. """ 8.60 8.61 - The actual images are created in the format <uuid>.img and <uuid>.qcow. 8.62 - """ 8.63 - 8.64 def __init__(self, uuid, 8.65 - sr_type = "qcow_file", 8.66 - name_label = "Local", 8.67 - name_description = "Xend Storage Repository", 8.68 - location = XEND_STORAGE_DIR, 8.69 + sr_type = "unknown", 8.70 + name_label = 'Unknown', 8.71 + name_description = 'Not Implemented', 8.72 + location = '', 8.73 storage_max = XEND_STORAGE_NO_MAXIMUM): 8.74 """ 8.75 @keyword storage_max: Maximum disk space to use in bytes. 8.76 @@ -91,303 +54,51 @@ class XendStorageRepository: 8.77 self.name_description = name_description 8.78 self.images = {} 8.79 8.80 - self.storage_max = storage_max 8.81 - self.storage_free = 0 8.82 - self.storage_used = 0 8.83 - self.storage_alloc = 0 8.84 + self.physical_size = storage_max 8.85 + self.physical_utilisation = 0 8.86 + self.virtual_allocation = 0 8.87 + 8.88 + self.lock = threading.RLock() 8.89 8.90 - self.lock = threading.RLock() 8.91 - self._refresh() 8.92 - 8.93 - def get_record(self): 8.94 + def get_record(self, transient = True): 8.95 retval = {'uuid': self.uuid, 8.96 'name_label': self.name_label, 8.97 'name_description': self.name_description, 8.98 - 'virtual_allocation': self.storage_alloc, 8.99 - 'physical_utilisation': self.storage_used, 8.100 - 'physical_size': self.storage_max, 8.101 + 'virtual_allocation': self.virtual_allocation, 8.102 + 'physical_utilisation': self.physical_utilisation, 8.103 + 'physical_size': self.physical_size, 8.104 'type': self.type, 8.105 'location': self.location, 8.106 'VDIs': self.images.keys()} 8.107 - 8.108 - if self.storage_max == XEND_STORAGE_NO_MAXIMUM: 8.109 - stfs = os.statvfs(self.location) 8.110 - retval['physical_size'] = stfs.f_blocks * stfs.f_frsize 8.111 8.112 return retval 8.113 - 8.114 - def _refresh(self): 8.115 - """Internal function that refreshes the state of the disk and 8.116 - updates the list of images available. 8.117 - """ 8.118 - self.lock.acquire() 8.119 - try: 8.120 - mkdir.parents(self.location, stat.S_IRWXU) 8.121 8.122 - # scan the directory and populate self.images 8.123 - virtual_alloc = 0 8.124 - physical_used = 0 8.125 - seen_images = [] 8.126 - for filename in os.listdir(self.location): 8.127 - if filename[-5:] == XEND_STORAGE_QCOW_FILENAME[-5:]: 8.128 - image_uuid = filename[:-5] 8.129 - seen_images.append(image_uuid) 8.130 - 8.131 - qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid 8.132 - cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid 8.133 - qcow_path = os.path.join(self.location, qcow_file) 8.134 - cfg_path = os.path.join(self.location, cfg_file) 8.135 - 8.136 - phys_size = os.stat(qcow_path).st_size 8.137 - virt_size = qcow_virtual_size(qcow_path) 8.138 - 8.139 - # add this image if we haven't seen it before 8.140 - if image_uuid not in self.images: 8.141 - vdi = XendQCOWVDI(image_uuid, self.uuid, 8.142 - qcow_path, cfg_path, 8.143 - virt_size, phys_size) 8.144 - 8.145 - if cfg_path and os.path.exists(cfg_path): 8.146 - try: 8.147 - vdi.load_config(cfg_path) 8.148 - except: 8.149 - log.error('Corrupt VDI configuration file %s' % 8.150 - cfg_path) 8.151 - 8.152 - self.images[image_uuid] = vdi 8.153 - 8.154 - physical_used += phys_size 8.155 - virtual_alloc += virt_size 8.156 - 8.157 - # remove images that aren't valid 8.158 - for image_uuid in self.images.keys(): 8.159 - if image_uuid not in seen_images: 8.160 - try: 8.161 - os.unlink(self.images[image_uuid].qcow_path) 8.162 - except OSError: 8.163 - pass 8.164 - del self.images[image_uuid] 8.165 - 8.166 - self.storage_alloc = virtual_alloc 8.167 - self.storage_used = physical_used 8.168 - 8.169 - # update free storage if we have to track that 8.170 - if self.storage_max == XEND_STORAGE_NO_MAXIMUM: 8.171 - self.storage_free = self._get_free_space() 8.172 - else: 8.173 - self.storage_free = self.storage_max - self.storage_alloc 8.174 - 8.175 - finally: 8.176 - self.lock.release() 8.177 - 8.178 - def _get_free_space(self): 8.179 - """Returns the amount of free space in bytes available in the storage 8.180 - partition. Note that this may not be used if the storage repository 8.181 - is initialised with a maximum size in storage_max. 8.182 - 8.183 - @rtype: int 8.184 - """ 8.185 - stfs = os.statvfs(self.location) 8.186 - return stfs.f_bavail * stfs.f_frsize 8.187 - 8.188 - def _has_space_available_for(self, size_bytes): 8.189 - """Returns whether there is enough space for an image in the 8.190 - partition which the storage_dir resides on. 8.191 - 8.192 - @rtype: bool 8.193 - """ 8.194 - if self.storage_max != XEND_STORAGE_NO_MAXIMUM: 8.195 - return self.storage_free > size_bytes 8.196 - 8.197 - bytes_free = self._get_free_space() 8.198 - if size_bytes < bytes_free: 8.199 - return True 8.200 - return False 8.201 - 8.202 - def _create_image_files(self, desired_size_bytes): 8.203 - """Create an image and return its assigned UUID. 8.204 - 8.205 - @param desired_size_bytes: Desired image size in bytes 8.206 - @type desired_size_bytes: int 8.207 - @rtype: string 8.208 - @return: uuid 8.209 - 8.210 - @raises XendError: If an error occurs. 8.211 - """ 8.212 - self.lock.acquire() 8.213 - try: 8.214 - if not self._has_space_available_for(desired_size_bytes): 8.215 - raise XendError("Not enough space") 8.216 - 8.217 - image_uuid = uuid.createString() 8.218 - qcow_path = os.path.join(self.location, 8.219 - XEND_STORAGE_QCOW_FILENAME % image_uuid) 8.220 - 8.221 - if qcow_path and os.path.exists(qcow_path): 8.222 - raise XendError("Image with same UUID alreaady exists:" % 8.223 - image_uuid) 8.224 - 8.225 - cmd = QCOW_CREATE_COMMAND % (desired_size_bytes/MB, qcow_path) 8.226 - rc, output = commands.getstatusoutput(cmd) 8.227 - 8.228 - if rc != 0: 8.229 - # cleanup the image file 8.230 - os.unlink(qcow_path) 8.231 - raise XendError("Failed to create QCOW Image: %s" % output) 8.232 - 8.233 - self._refresh() 8.234 - return image_uuid 8.235 - finally: 8.236 - self.lock.release() 8.237 - 8.238 - def destroy_image(self, image_uuid): 8.239 - """Destroy an image that is managed by this storage repository. 8.240 - 8.241 - @param image_uuid: Image UUID 8.242 - @type image_uuid: String 8.243 - @rtype: String 8.244 - """ 8.245 - self.lock.acquire() 8.246 - try: 8.247 - if image_uuid in self.images: 8.248 - # TODO: check if it is being used? 8.249 - qcow_path = self.images[image_uuid].qcow_path 8.250 - cfg_path = self.images[image_uuid].cfg_path 8.251 - try: 8.252 - os.unlink(qcow_path) 8.253 - if cfg_path and os.path.exists(cfg_path): 8.254 - os.unlink(cfg_path) 8.255 - except OSError: 8.256 - log.exception("Failed to destroy image") 8.257 - del self.images[image_uuid] 8.258 - return True 8.259 - finally: 8.260 - self.lock.release() 8.261 - 8.262 - return False 8.263 - 8.264 - def list_images(self): 8.265 - """ List all the available images by UUID. 8.266 - 8.267 - @rtype: list of strings. 8.268 - @return: list of UUIDs 8.269 - """ 8.270 - self.lock.acquire() 8.271 - try: 8.272 - return self.images.keys() 8.273 - finally: 8.274 - self.lock.release() 8.275 - 8.276 - def free_space_bytes(self): 8.277 - """Returns the amount of available space in KB. 8.278 - @rtype: int 8.279 - """ 8.280 - self.lock.acquire() 8.281 - try: 8.282 - return self.storage_free 8.283 - finally: 8.284 - self.lock.release() 8.285 - 8.286 - def total_space_bytes(self): 8.287 - """Returns the total usable space of the storage repo in KB. 8.288 - @rtype: int 8.289 - """ 8.290 - self.lock.acquire() 8.291 - try: 8.292 - if self.storage_max == XEND_STORAGE_NO_MAXIMUM: 8.293 - stfs = os.statvfs(self.location) 8.294 - return stfs.f_blocks * stfs.f_frsize 8.295 - else: 8.296 - return self.storage_max 8.297 - finally: 8.298 - self.lock.release() 8.299 - 8.300 - def used_space_bytes(self): 8.301 - """Returns the total amount of space used by this storage repository. 8.302 - @rtype: int 8.303 - """ 8.304 - self.lock.acquire() 8.305 - try: 8.306 - return self.storage_used 8.307 - finally: 8.308 - self.lock.release() 8.309 - 8.310 - def virtual_allocation(self): 8.311 - """Returns the total virtual space allocated within the storage repo. 8.312 - @rtype: int 8.313 - """ 8.314 - self.lock.acquire() 8.315 - try: 8.316 - return self.storage_alloc 8.317 - finally: 8.318 - self.lock.release() 8.319 8.320 def is_valid_vdi(self, vdi_uuid): 8.321 return (vdi_uuid in self.images) 8.322 8.323 - def create_image(self, vdi_struct): 8.324 - image_uuid = None 8.325 - try: 8.326 - sector_count = int(vdi_struct.get('virtual_size', 0)) 8.327 - sector_size = int(vdi_struct.get('sector_size', 1024)) 8.328 - size_bytes = (sector_count * sector_size) 8.329 - 8.330 - image_uuid = self._create_image_files(size_bytes) 8.331 - image = self.images[image_uuid] 8.332 - image_cfg = { 8.333 - 'sector_size': sector_size, 8.334 - 'virtual_size': sector_count, 8.335 - 'type': vdi_struct.get('type', 'system'), 8.336 - 'name_label': vdi_struct.get('name_label', ''), 8.337 - 'name_description': vdi_struct.get('name_description', ''), 8.338 - 'sharable': bool(vdi_struct.get('sharable', False)), 8.339 - 'read_only': bool(vdi_struct.get('read_only', False)), 8.340 - } 8.341 - 8.342 - # load in configuration from vdi_struct 8.343 - image.load_config_dict(image_cfg) 8.344 - 8.345 - # save configuration to file 8.346 - cfg_filename = XEND_STORAGE_VDICFG_FILENAME % image_uuid 8.347 - cfg_path = os.path.join(self.location, cfg_filename) 8.348 - image.save_config(cfg_path) 8.349 - 8.350 - except Exception, e: 8.351 - # cleanup before raising exception 8.352 - if image_uuid: 8.353 - self.destroy_image(image_uuid) 8.354 - 8.355 - raise 8.356 - 8.357 - return image_uuid 8.358 - 8.359 - def xen_api_get_by_name_label(self, label): 8.360 + def get_vdi_by_uuid(self, image_uuid): 8.361 self.lock.acquire() 8.362 try: 8.363 - for image_uuid, val in self.images.items(): 8.364 - if val.name_label == label: 8.365 + return self.images.get(image_uuid) 8.366 + finally: 8.367 + self.lock.release() 8.368 + 8.369 + def get_vdi_by_name_label(self, label): 8.370 + self.lock.acquire() 8.371 + try: 8.372 + for image_uuid, image in self.images.items(): 8.373 + if image.name_label == label: 8.374 return image_uuid 8.375 return None 8.376 finally: 8.377 self.lock.release() 8.378 8.379 - def xen_api_get_by_uuid(self, image_uuid): 8.380 - self.lock.acquire() 8.381 - try: 8.382 - return self.images.get(image_uuid) 8.383 - finally: 8.384 - self.lock.release() 8.385 - 8.386 + def get_vdis(self): 8.387 + return self.images.keys() 8.388 8.389 -# remove everything below this line!! 8.390 -if __name__ == "__main__": 8.391 - xsr = XendStorageRepository() 8.392 - print 'Free Space: %d MB' % (xsr.free_space_bytes()/MB) 8.393 - print "Create Image:", 8.394 - print xsr._create_image_files(10 * MB) 8.395 - print 'Delete all images:' 8.396 - for image_uuid in xsr.list_images(): 8.397 - print image_uuid, 8.398 - xsr._destroy_image_files(image_uuid) 8.399 + def create_vdi(self, vdi_struct): 8.400 + raise NotImplementedError() 8.401 8.402 - print 8.403 + def destroy_vdi(self, vdi_struct): 8.404 + raise NotImplementedError()
9.1 --- a/tools/python/xen/xend/XendVDI.py Wed Jan 24 14:25:21 2007 +0000 9.2 +++ b/tools/python/xen/xend/XendVDI.py Wed Jan 24 15:47:31 2007 +0000 9.3 @@ -141,7 +141,7 @@ class XendVDI(AutoSaveObject): 9.4 9.5 return True 9.6 9.7 - def get_record(self): 9.8 + def get_record(self, transient = True): 9.9 return {'uuid': self.uuid, 9.10 'name_label': self.name_label, 9.11 'name_description': self.name_description, 9.12 @@ -152,12 +152,14 @@ class XendVDI(AutoSaveObject): 9.13 'children': [], 9.14 'sharable': False, 9.15 'readonly': False, 9.16 - 'SR': self.sr.get_uuid(), 9.17 + 'SR': self.sr_uuid, 9.18 'VBDs': []} 9.19 + 9.20 + def get_image_uri(self): 9.21 + raise NotImplementedError() 9.22 9.23 9.24 -class XendQCOWVDI(XendVDI): 9.25 - 9.26 +class XendQCoWVDI(XendVDI): 9.27 def __init__(self, uuid, sr_uuid, qcow_path, cfg_path, vsize, psize): 9.28 XendVDI.__init__(self, uuid, sr_uuid) 9.29 self.auto_save = False 9.30 @@ -168,3 +170,26 @@ class XendQCOWVDI(XendVDI): 9.31 self.sector_size = 512 9.32 self.auto_save = True 9.33 9.34 + def get_image_uri(self): 9.35 + return 'tap:qcow:%s' % self.qcow_path 9.36 + 9.37 +class XendLocalVDI(XendVDI): 9.38 + def __init__(self, vdi_struct): 9.39 + vdi_uuid = vdi_struct['uuid'] 9.40 + sr_uuid = vdi_struct['SR'] 9.41 + XendVDI.__init__(self, vdi_uuid, sr_uuid) 9.42 + 9.43 + self.auto_save = False 9.44 + self.cfg_path = None 9.45 + self.name_label = vdi_struct.get('name_label','') 9.46 + self.name_description = vdi_struct.get('name_description', '') 9.47 + self.physical_utilisation = 0 9.48 + self.virtual_size = 0 9.49 + self.sector_size = 0 9.50 + self.type = vdi_struct.get('type', '') 9.51 + self.sharable = vdi_struct.get('sharable', False) 9.52 + self.read_only = vdi_struct.get('read_only', False) 9.53 + self.image_uri = vdi_struct.get('uri', 'file:/dev/null') 9.54 + 9.55 + def get_image_uri(self): 9.56 + return self.image_uri