[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [XEND] Beginnings of the Xend Storage Repository implementation.
# HG changeset patch # User Alastair Tse <atse@xxxxxxxxxxxxx> # Node ID 1b923f4a788a58b8c7e81d0866058e7e4d18c47c # Parent 255925ae4127287e2cd49bbc06f33965d31f7fb5 [XEND] Beginnings of the Xend Storage Repository implementation. Currently it is a basic repository that just creates images on demand and wraps them as VDIs. Rudimentary support for querying the available space using 'df'. Creates images via 'qcow-create' after creating an empty image. Signed-off-by: Alastair Tse <atse@xxxxxxxxxxxxx> --- tools/python/xen/xend/XendNode.py | 2 tools/python/xen/xend/XendStorageRepository.py | 327 +++++++++++++++++++++++++ tools/python/xen/xend/XendVDI.py | 44 +++ 3 files changed, 373 insertions(+) diff -r 255925ae4127 -r 1b923f4a788a tools/python/xen/xend/XendNode.py --- a/tools/python/xen/xend/XendNode.py Thu Oct 12 18:00:01 2006 +0100 +++ b/tools/python/xen/xend/XendNode.py Thu Oct 12 18:01:32 2006 +0100 @@ -21,6 +21,7 @@ import xen.lowlevel.xc import xen.lowlevel.xc from xen.xend import uuid from xen.xend.XendError import XendError +from xen.xend.XendStorageRepository import XendStorageRepository class XendNode: """XendNode - Represents a Domain 0 Host.""" @@ -31,6 +32,7 @@ class XendNode: self.cpus = {} self.name = socket.gethostname() self.desc = "" + self.sr = XendStorageRepository() physinfo = self.physinfo_dict() cpu_count = physinfo['nr_cpus'] diff -r 255925ae4127 -r 1b923f4a788a tools/python/xen/xend/XendStorageRepository.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xend/XendStorageRepository.py Thu Oct 12 18:01:32 2006 +0100 @@ -0,0 +1,327 @@ +#!/usr/bin/python +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 XenSource Ltd. +#============================================================================ +# +# The default QCOW Xen API Storage Repository +# + +import os +import commands +import threading + +from xen.xend import uuid +from xen.xend.XendError import XendError +from xen.xend.XendVDI import * + +XEND_STORAGE_MAX_IGNORE = -1 +XEND_STORAGE_DIR = "/var/lib/xend/storage/" +XEND_STORAGE_QCOW_FILENAME = "%s.qcow" +XEND_STORAGE_IMG_FILENAME = "%s.img" +DF_COMMAND = "df -kl" +QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create %d %s %s" + +KB = 1024 +MB = 1024 *1024 + +class DeviceInvalidError(Exception): + pass + +class XendStorageRepository: + """A simple file backed QCOW Storage Repository. + + This class exposes the interface to create VDI's via the + Xen API. The backend is a file-backed QCOW format that is stored + in XEND_STORAGE_DIR or any that is specified in the constructor. + + The actual images are created in the format <uuid>.img and <uuid>.qcow. + """ + + def __init__(self, storage_dir = XEND_STORAGE_DIR, + storage_max = XEND_STORAGE_MAX_IGNORE): + """ + @keyword storage_dir: Where the images will be stored. + @type storage_dir: string + @keyword storage_max: Maximum disk space to use in KB. + @type storage_max: int + + @ivar storage_free: storage space free for this repository + @ivar images: mapping of all the images. + @type images: dictionary by image uuid. + @ivar lock: lock to provide thread safety. + """ + + self.storage_dir = storage_dir + self.storage_max = storage_max + self.storage_free = 0 + self.images = {} + + # XenAPI Parameters + self.uuid = self._sr_uuid() + self.type = "qcow-file" + self.location = self.storage_dir + self.name_label = "Local" + self.name_description = "Xend Storage Repository" + + self.lock = threading.RLock() + self._refresh() + + def _sr_uuid(self): + uuid_file = os.path.join(XEND_STORAGE_DIR, 'uuid') + try: + if os.path.exists(uuid_file): + return open(uuid_file, 'r').read().strip() + else: + new_uuid = uuid.createString() + open(uuid_file, 'w').write(new_uuid + '\n') + return new_uuid + except IOError: + # TODO: log warning + pass + + return uuid.createString() + + def _refresh(self): + """Internal function that refreshes the state of the disk and + updates the list of images available. + """ + self.lock.acquire() + try: + if not os.path.exists(XEND_STORAGE_DIR): + os.makedirs(XEND_STORAGE_DIR) + os.chmod(XEND_STORAGE_DIR, 0700) + + # scan the directory and populate self.images + total_used = 0 + seen_images = [] + for filename in os.listdir(XEND_STORAGE_DIR): + if filename[-5:] == XEND_STORAGE_QCOW_FILENAME[-5:]: + image_uuid = filename[:-5] + seen_images.append(image_uuid) + if image_uuid not in self.images: + image_file = XEND_STORAGE_IMG_FILENAME % image_uuid + qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid + image_path = os.path.join(XEND_STORAGE_DIR, + image_file) + qcow_path = os.path.join(XEND_STORAGE_DIR, qcow_file) + image_size_kb = (os.stat(image_path).st_size)/1024 + + vdi = XendQCOWVDI(image_uuid, self.uuid, + qcow_path, image_path, + image_size_kb, image_size_kb) + self.images[image_uuid] = vdi + total_used += image_size_kb + + # remove images that aren't valid + for image_uuid in self.images.keys(): + if image_uuid not in seen_images: + try: + os.unlink(self.images[image_uuid].qcow_path) + os.unlink(self.images[image_uuid].image_path) + except OSError: + pass + del self.images[image_uuid] + + # update free storage if we have to track that + if self.storage_max != XEND_STORAGE_MAX_IGNORE: + self.storage_free = self.storage_max - total_used + else: + self.storage_free = self._get_free_space() + + finally: + self.lock.release() + + def _get_df(self): + """Returns the output of 'df' in a dictionary where the keys + are the Linux device numbers, and the values are it's corresponding + free space in KB. + + @rtype: dictionary + """ + df = commands.getoutput(DF_COMMAND) + devnum_free = {} + for line in df.split('\n')[1:]: + words = line.split() + mount_point = words[-1] + dev_no = os.stat(mount_point).st_dev + free_blks = int(words[3]) + devnum_free[dev_no] = free_blks + return devnum_free + + def _get_free_space(self): + """Returns the amount of free space in KB available in the storage + partition. Note that this may not be used if the storage repository + is initialised with a maximum size in storage_max. + + @rtype: int + """ + df = self._get_df() + devnum = os.stat(self.storage_dir).st_dev + if df.has_key(devnum): + return df[devnum] + raise DeviceInvalidError("Device not found for storage path: %s" % + self.storage_dir) + + def _has_space_available_for(self, size_kb): + """Returns whether there is enough space for an image in the + partition which the storage_dir resides on. + + @rtype: bool + """ + if self.storage_max != -1: + return self.storage_free + + kb_free = self._get_free_space() + try: + if size_kb < kb_free: + return True + except DeviceInvalidError: + pass + return False + + def create_image(self, desired_size_kb): + """Create an image and return its assigned UUID. + + @param desired_size_kb: Desired image size in KB. + @type desired_size_kb: int + @rtype: string + @return: uuid + + @raises XendError: If an error occurs. + """ + self.lock.acquire() + try: + if not self._has_space_available_for(desired_size_kb): + raise XendError("Not enough space") + + image_uuid = uuid.createString() + # create file based image + image_path = os.path.join(XEND_STORAGE_DIR, + XEND_STORAGE_IMG_FILENAME % image_uuid) + block = '\x00' * 1024 + img = open(image_path, 'w') + for i in range(desired_size_kb): + img.write(block) + img.close() + + # TODO: create qcow image + qcow_path = os.path.join(XEND_STORAGE_DIR, + XEND_STORAGE_QCOW_FILENAME % image_uuid) + cmd = QCOW_CREATE_COMMAND % (desired_size_kb/1024, + qcow_path, image_path) + + rc, output = commands.getstatusoutput(cmd) + if rc != 0: + # cleanup the image file + os.unlink(image_path) + raise XendError("Failed to create QCOW Image: %s" % output) + + self._refresh() + return image_uuid + finally: + self.lock.release() + + def destroy_image(self, image_uuid): + """Destroy an image that is managed by this storage repository. + + @param image_uuid: Image UUID + @type image_uuid: String + @rtype: String + """ + self.lock.acquire() + try: + if image_uuid in self.images: + # TODO: check if it is being used? + qcow_path = self.images[image_uuid].qcow_path + image_path = self.images[image_uuid].image_path + try: + os.unlink(qcow_path) + os.unlink(image_path) + except OSError: + # TODO: log warning + pass + del self.images[image_uuid] + return True + finally: + self.lock.release() + + return False + + def list_images(self): + """ List all the available images by UUID. + + @rtype: list of strings. + @return: list of UUIDs + """ + self.lock.acquire() + try: + return self.images.keys() + finally: + self.lock.release() + + def free_space_kb(self): + """Returns the amount of available space in KB. + @rtype: int + """ + self.lock.acquire() + try: + return self.storage_free + finally: + self.lock.release() + + def total_space_kb(self): + """Returns the total usable space of the storage repo in KB. + @rtype: int + """ + self.lock.acquire() + try: + if self.storage_max != XEND_STORAGE_MAX_IGNORE: + return self.storage_max + else: + return self.free_space_kb() + self.used_space_kb() + finally: + self.lock.release() + + def used_space_kb(self): + """Returns the total amount of space used by this storage repository. + @rtype: int + """ + self.lock.acquire() + try: + total_used = 0 + for val in self.images.values(): + total_used += val.get_physical_utilisation() + return total_used + finally: + self.lock.release() + + def used_space_bytes(self): + return self.used_space_kb() * KB + def free_space_bytes(self): + return self.free_space_kb() * KB + def total_space_bytes(self): + return self.total_space_kb() * KB + + +# remove everything below this line!! +if __name__ == "__main__": + xsr = XendStorageRepository() + print 'Free Space: %d MB' % (xsr.free_space_kb()/1024) + print "Create Image:", + print xsr.create_image(10 * 1024) + print 'Delete all images:' + for image_uuid in xsr.list_images(): + xsr.destroy_image(image_uuid) diff -r 255925ae4127 -r 1b923f4a788a tools/python/xen/xend/XendVDI.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xend/XendVDI.py Thu Oct 12 18:01:32 2006 +0100 @@ -0,0 +1,44 @@ +#!/usr/bin/python +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 XenSource Ltd. +#============================================================================ +# +# Representation of a Xen API VDI +# + +KB = 1024 +MB = 1024 * 1024 + +class XendVDI: + def __init__(self, uuid, sr_uuid): + self.uuid = uuid + self.sr_uuid = sr_uuid + +class XendQCOWVDI(XendVDI): + vdi_type = "system" + + def __init__(self, uuid, sr_uuid, qcow_path, image_path, vsize, psize): + XendVDI.__init__(self, uuid, sr_uuid) + self.qcow_path = qcow_path + self.image_path = image_path + self.vsize = vsize + self.psize = psize + + def get_physical_utilisation(self): + return self.psize * KB + + def get_virtual_size(self): + return self.vsize * KB _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |