ia64/xen-unstable

changeset 13600: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>
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