WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-changelog

[Xen-changelog] [xen-unstable] [XEND] Beginnings of the Xend Storage Rep

To: xen-changelog@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-changelog] [xen-unstable] [XEND] Beginnings of the Xend Storage Repository implementation.
From: Xen patchbot-unstable <patchbot-unstable@xxxxxxxxxxxxxxxxxxx>
Date: Thu, 02 Nov 2006 22:08:56 +0000
Delivery-date: Thu, 02 Nov 2006 21:42:21 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-changelog-request@lists.xensource.com?subject=help>
List-id: BK change log <xen-changelog.lists.xensource.com>
List-post: <mailto:xen-changelog@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=unsubscribe>
Reply-to: xen-devel@xxxxxxxxxxxxxxxxxxx
Sender: xen-changelog-bounces@xxxxxxxxxxxxxxxxxxx
# 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

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-changelog] [xen-unstable] [XEND] Beginnings of the Xend Storage Repository implementation., Xen patchbot-unstable <=