[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] Plumb the new PIF and network implementations in through the XendAPI class.



# HG changeset patch
# User Ewan Mellor <ewan@xxxxxxxxxxxxx>
# Date 1167059068 0
# Node ID 765ada5f74cc21fffe442b09d675dc0fe5ce4dcf
# Parent  4fbefd9cb85e42fbf3e13c48675983696ac45221
Plumb the new PIF and network implementations in through the XendAPI class.
Use the new stateful records to save host, SR, network, and PIF configuration.
Add extra functionality to the SR class, to calculate capacities et al.

By Alastair Tse <atse@xxxxxxxxxxxxx>.

Signed-off-by: Ewan Mellor <ewan@xxxxxxxxxxxxx>
---
 tools/python/xen/xend/XendAPI.py               |  126 ++++++++++++++++---
 tools/python/xen/xend/XendNode.py              |  134 ++++++++++++++++++---
 tools/python/xen/xend/XendStorageRepository.py |  159 +++++++++++++------------
 3 files changed, 313 insertions(+), 106 deletions(-)

diff -r 4fbefd9cb85e -r 765ada5f74cc tools/python/xen/xend/XendAPI.py
--- a/tools/python/xen/xend/XendAPI.py  Mon Dec 25 14:59:11 2006 +0000
+++ b/tools/python/xen/xend/XendAPI.py  Mon Dec 25 15:04:28 2006 +0000
@@ -266,6 +266,29 @@ def valid_sr(func):
 
     return check_sr_ref
 
+
+def valid_pif(func):
+    """Decorator to verify if sr_ref is valid before calling
+    method.
+
+    @param func: function with params: (self, session, sr_ref)
+    @rtype: callable object
+    """
+    def check_pif_ref(self, session, pif_ref, *args, **kwargs):
+        xennode = XendNode.instance()
+        if type(pif_ref) == type(str()) and pif_ref in xennode.pifs:
+            return func(self, session, pif_ref, *args, **kwargs)
+        else:
+            return xen_api_error(['PIF_HANDLE_INVALID', pif_ref])
+
+    # make sure we keep the 'api' attribute
+    if hasattr(func, 'api'):
+        check_pif_ref.api = func.api
+        
+    return check_pif_ref
+
+
+
 # -----------------------------
 # Bridge to Legacy XM API calls
 # -----------------------------
@@ -477,15 +500,80 @@ class XendAPI:
 
     # Xen API: Class Network
     # ----------------------------------------------------------------
-    # TODO: NOT IMPLEMENTED
-
-    Network_attr_ro = ['VIFs']
+
+    Network_attr_ro = ['VIFs', 'PIFs']
     Network_attr_rw = ['name_label',
                        'name_description',
-                       'NIC',
-                       'VLAN',
                        'default_gateway',
                        'default_netmask']
+
+    # Xen API: Class PIF
+    # ----------------------------------------------------------------
+
+    PIF_attr_ro = ['io_read_kbs',
+                   'io_write_kbs']
+    PIF_attr_rw = ['name',
+                   'network',
+                   'host',
+                   'MAC',
+                   'MTU',
+                   'VLAN']
+
+    PIF_attr_inst = PIF_attr_rw
+
+    # object methods
+    def PIF_get_record(self, session, pif_ref):
+        node = XendNode.instance()
+        return xen_api_success(node.pifs[pif_ref].get_record())
+
+    def PIF_get_all(self, session):
+        return xen_api_success(XendNode.instance().pifs.keys())
+
+    def PIF_set_name(self, session, pif_ref, name):
+        node = XendNode.instance()
+        pif = node.pifs.get(pif_ref)
+        if pif:
+            pif.set_name(name)
+        return xen_api_void()        
+
+    def PIF_set_mac(self, session, pif_ref, mac):
+        node = XendNode.instance()
+        pif = node.pifs.get(pif_ref)
+        if pif:
+            pif.set_mac(mac)
+        return xen_api_void()
+
+    def PIF_set_mtu(self, session, pif_ref, mtu):
+        node = XendNode.instance()
+        pif = node.pifs.get(pif_ref)
+        if pif:
+            pif.set_mtu(mtu)
+        return xen_api_void()    
+
+    def PIF_get_mac(self, session, pif_ref):
+        node = XendNode.instance()
+        return xen_api_success(node.pifs[pif_ref].get_mac())
+
+    def PIF_get_mtu(self, session, pif_ref):
+        node = XendNode.instance()
+        return xen_api_success(node.pifs[pif_ref].get_mtu())
+
+    def PIF_get_vlan(self, session, pif_ref):
+        node = XendNode.instance()
+        return xen_api_success(node.pifs[pif_ref].get_vlan())
+
+    def PIF_get_name(self, session, pif_ref):
+        node = XendNode.instance()
+        return xen_api_success(node.pifs[pif_ref].get_name())
+
+    def PIF_get_io_read_kbs(self, session, pif_ref):
+        node = XendNode.instance()
+        return xen_api_success(node.pifs[pif_ref].get_io_read_kbs())
+
+    def PIF_get_io_write_kbs(self, session, pif_ref):
+        node = XendNode.instance()
+        return xen_api_success(node.pifs[pif_ref].get_io_write_kbs())    
+    
 
     # Xen API: Class VM
     # ----------------------------------------------------------------        
@@ -1189,9 +1277,16 @@ class XendAPI:
         return xen_api_error(XEND_ERROR_UNSUPPORTED)
 
     def VDI_set_sharable(self, session, vdi_ref, value):
-        return xen_api_todo()
+        sr = XendNode.instance().get_sr()
+        image = sr.xen_api_get_by_uuid(vdi_ref)
+        image.sharable = bool(value)
+        return xen_api_success_void()
+    
     def VDI_set_read_only(self, session, vdi_ref, value):
-        return xen_api_todo()
+        sr = XendNode.instance().get_sr()
+        image = sr.xen_api_get_by_uuid(vdi_ref)
+        image.read_only = bool(value)
+        return xen_api_success_void()
 
     # Object Methods
     def VDI_snapshot(self, session, vdi_ref):
@@ -1383,17 +1478,7 @@ class XendAPI:
     
     def SR_get_record(self, session, sr_ref):
         sr = XendNode.instance().get_sr()
-        return xen_api_success({
-            'uuid': sr.uuid,
-            'name_label': sr.name_label,
-            'name_description': sr.name_description,
-            'VDIs': sr.list_images(),
-            'virtual_allocation': sr.used_space_bytes(),
-            'physical_utilisation': sr.used_space_bytes(),
-            'physical_size': sr.total_space_bytes(),
-            'type': sr.type,
-            'location': sr.location
-            })
+        return xen_api_success(sr.get_record())
 
     # Attribute acceess
     def SR_get_VDIs(self, session, sr_ref):
@@ -1431,11 +1516,13 @@ class XendAPI:
     def SR_set_name_label(self, session, sr_ref, value):
         sr = XendNode.instance().get_sr()
         sr.name_label = value
+        XendNode.instance().save()
         return xen_api_success_void()
     
     def SR_set_name_description(self, session, sr_ref, value):
         sr = XendNode.instance().get_sr()
         sr.name_description = value
+        XendNode.instance().save()        
         return xen_api_success_void()
 
 
@@ -1454,7 +1541,8 @@ def _decorate():
         'VIF': (valid_vif, session_required, catch_typeerror),
         'VDI': (valid_vdi, session_required, catch_typeerror),
         'VTPM':(valid_vtpm, session_required, catch_typeerror),
-        'SR':  (valid_sr, session_required, catch_typeerror)}
+        'SR':  (valid_sr, session_required, catch_typeerror),
+        'PIF': (valid_pif, session_required, catch_typeerror)}
 
     # Cheat methods
     # -------------
diff -r 4fbefd9cb85e -r 765ada5f74cc tools/python/xen/xend/XendNode.py
--- a/tools/python/xen/xend/XendNode.py Mon Dec 25 14:59:11 2006 +0000
+++ b/tools/python/xen/xend/XendNode.py Mon Dec 25 15:04:28 2006 +0000
@@ -21,32 +21,128 @@ import xen.lowlevel.xc
 import xen.lowlevel.xc
 from xen.xend import uuid
 from xen.xend.XendError import XendError
+from xen.xend.XendRoot import instance as xendroot
 from xen.xend.XendStorageRepository import XendStorageRepository
+from xen.xend.XendLogging import log
+from xen.xend.XendPIF import *
+from xen.xend.XendNetwork import *
+from xen.xend.XendStateStore import XendStateStore
 
 class XendNode:
     """XendNode - Represents a Domain 0 Host."""
     
     def __init__(self):
+        """Initalises the state of all host specific objects such as
+
+        * Host
+        * Host_CPU
+        * PIF
+        * Network
+        * Storage Repository
+        """
+        
         self.xc = xen.lowlevel.xc.xc()
-        self.uuid = uuid.createString()
-        self.cpus = {}
-        self.name = socket.gethostname()
-        self.desc = ""
-        self.sr = XendStorageRepository()
-        
+        self.state_store = XendStateStore(xendroot().get_xend_state_path())
+
+        # load host state from XML file
+        saved_host = self.state_store.load_state('host')
+        if saved_host and len(saved_host.keys()) == 1:
+            self.uuid = saved_host.keys()[0]
+            host = saved_host[self.uuid]
+            self.name = host.get('name_label', socket.gethostname())
+            self.desc = host.get('name_description', '')
+            self.cpus = {}
+        else:
+            self.uuid = uuid.createString()
+            self.name = socket.gethostname()
+            self.desc = ''
+            self.cpus = {}
+            
+        # load CPU UUIDs
+        saved_cpus = self.state_store.load_state('cpu')
+        for cpu_uuid, cpu in saved_cpus.items():
+            self.cpus[cpu_uuid] = cpu
+
+        # verify we have enough cpus here
         physinfo = self.physinfo_dict()
         cpu_count = physinfo['nr_cpus']
         cpu_features = physinfo['hw_caps']
-        
-        for i in range(cpu_count):
-            # construct uuid by appending extra bit on the host.
-            # since CPUs belong to a host.
-            cpu_uuid = self.uuid + '-%04d' % i
-            cpu_info = {'uuid': cpu_uuid,
-                        'host': self.uuid,
-                        'number': i,
-                        'features': cpu_features}
-            self.cpus[cpu_uuid] = cpu_info
+
+        # If the number of CPUs don't match, we should just reinitialise 
+        # the CPU UUIDs.
+        if cpu_count != len(self.cpus):
+            self.cpus = {}
+            for i in range(cpu_count):
+                cpu_uuid = uuid.createString()
+                cpu_info = {'uuid': cpu_uuid,
+                            'host': self.uuid,
+                            'number': i,
+                            'features': cpu_features}
+                self.cpus[cpu_uuid] = cpu_info
+
+        self.pifs = {}
+        self.networks = {}
+
+        # initialise PIFs
+        saved_pifs = self.state_store.load_state('pif')
+        if saved_pifs:
+            for pif_uuid, pif in saved_pifs.items():
+                self.pifs[pif_uuid] = XendPIF(pif_uuid,
+                                              pif['name'],
+                                              pif['MTU'],
+                                              pif['MAC'])
+        else:
+            for name, mtu, mac in linux_get_phy_ifaces():
+                pif_uuid = uuid.createString()
+                pif = XendPIF(pif_uuid, name, mtu, mac)
+                self.pifs[pif_uuid] = pif
+
+        # initialise networks
+        saved_networks = self.state_store.load_state('network')
+        if saved_networks:
+            for net_uuid, network in saved_networks.items():
+                self.networks[net_uuid] = XendNetwork(net_uuid,
+                                network.get('name_label'),
+                                network.get('name_description', ''),
+                                network.get('default_gateway', ''),
+                                network.get('default_netmask', ''))
+                
+                for pif_uuid in network.get('PIFs', {}).keys():
+                    pif = self.pifs.get(pif_uuid)
+                    if pif:
+                        self.networks.pifs[pif_uuid] = pif
+        else:
+            gateway, netmask = linux_get_default_network()
+            net_uuid = uuid.createString()
+            net = XendNetwork(net_uuid, 'net0', '', gateway, netmask)
+            self.networks[net_uuid] = net
+
+        # initialise storage
+        saved_sr = self.state_store.load_state('sr')
+        if saved_sr and len(saved_sr) == 1:
+            sr_uuid = saved_sr.keys()[0]
+            self.sr = XendStorageRepository(sr_uuid)
+        else:
+            sr_uuid = uuid.createString()
+            self.sr = XendStorageRepository(sr_uuid)
+        self.save()
+
+    def save(self):
+        # save state
+        host_record = {self.uuid: {'name_label':self.name,
+                                   'name_description':self.desc}}
+        self.state_store.save_state('host',host_record)
+        self.state_store.save_state('cpu', self.cpus)
+        pif_records = dict([(k, v.get_record())
+                            for k, v in self.pifs.items()])
+        self.state_store.save_state('pif', pif_records)
+        net_records = dict([(k, v.get_record())
+                            for k, v in self.networks.items()])
+        self.state_store.save_state('network', net_records)
+
+
+        sr_record = {self.sr.uuid: self.sr.get_record()}
+        self.state_store.save_state('sr', sr_record)
 
     def shutdown(self):
         return 0
@@ -56,7 +152,8 @@ class XendNode:
 
     def notify(self, _):
         return 0
-    
+
+        
     #
     # Ref validation
     #
@@ -99,6 +196,9 @@ class XendNode:
 
     def set_description(self, new_desc):
         self.desc = new_desc
+
+    def get_uuid(self):
+        return self.uuid
 
     #
     # Host CPU Functions
diff -r 4fbefd9cb85e -r 765ada5f74cc 
tools/python/xen/xend/XendStorageRepository.py
--- a/tools/python/xen/xend/XendStorageRepository.py    Mon Dec 25 14:59:11 
2006 +0000
+++ b/tools/python/xen/xend/XendStorageRepository.py    Mon Dec 25 15:04:28 
2006 +0000
@@ -24,13 +24,17 @@ import os
 import os
 import stat
 import threading
+import re
+import sys
+import struct
 
 from xen.util import mkdir
 from xen.xend import uuid
 from xen.xend.XendError import XendError
 from xen.xend.XendVDI import *
 
-XEND_STORAGE_MAX_IGNORE = -1
+
+XEND_STORAGE_NO_MAXIMUM = sys.maxint
 XEND_STORAGE_DIR = "/var/lib/xend/storage/"
 XEND_STORAGE_QCOW_FILENAME = "%s.qcow"
 XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml"
@@ -41,8 +45,17 @@ log = logging.getLogger("xend.XendStorag
 log = logging.getLogger("xend.XendStorageRepository")
 
 
-class DeviceInvalidError(Exception):
-    pass
+def qcow_virtual_size(qcow_file):
+    """Read the first 32 bytes of the QCoW header to determine its size.
+
+    See: http://www.gnome.org/~markmc/qcow-image-format.html.
+    """
+    try:
+        qcow_header = open(qcow_file, 'rb').read(32)
+        parts = struct.unpack('>IIQIIQ', qcow_header)
+        return parts[-1]
+    except IOError:
+        return -1
 
 class XendStorageRepository:
     """A simple file backed QCOW Storage Repository.
@@ -54,11 +67,13 @@ class XendStorageRepository:
     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
+    def __init__(self, uuid,
+                 sr_type = "qcow_file",
+                 name_label = "Local",
+                 name_description = "Xend Storage Repository",
+                 location = XEND_STORAGE_DIR,
+                 storage_max = XEND_STORAGE_NO_MAXIMUM):
+        """
         @keyword storage_max: Maximum disk space to use in bytes.
         @type    storage_max: int
 
@@ -67,71 +82,78 @@ class XendStorageRepository:
         @type    images: dictionary by image uuid.
         @ivar    lock:   lock to provide thread safety.
         """
-        
-        self.storage_dir = storage_dir
+
+        # XenAPI Parameters
+        self.uuid = uuid
+        self.type = sr_type
+        self.location = location
+        self.name_label = name_label
+        self.name_description = name_description
+        self.images = {}
+
         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.storage_used = 0
+        self.storage_alloc = 0
 
         self.lock = threading.RLock()
-        self._refresh()        
-
-    def _sr_uuid(self):
-        uuid_file = os.path.join(XEND_STORAGE_DIR, 'uuid')
-        try:
-            if uuid_file and 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:
-            log.exception("Failed to determine SR UUID")
-
-        return uuid.createString()
-
+        self._refresh()
+
+    def get_record(self):
+        retval = {'uuid': self.uuid,
+                  'name_label': self.name_label,
+                  'name_description': self.name_description,
+                  'virtual_allocation': self.storage_alloc,
+                  'physical_utilisation': self.storage_used,
+                  'physical_size': self.storage_max,
+                  'type': self.type,
+                  'location': self.location,
+                  'VDIs': self.images.keys()}
+        
+        if self.storage_max == XEND_STORAGE_NO_MAXIMUM:
+            stfs = os.statvfs(self.location)
+            retval['physical_size'] = stfs.f_blocks * stfs.f_frsize
+
+        return retval
+        
     def _refresh(self):
         """Internal function that refreshes the state of the disk and
         updates the list of images available.
         """
         self.lock.acquire()
         try:
-            mkdir.parents(XEND_STORAGE_DIR, stat.S_IRWXU)
+            mkdir.parents(self.location, stat.S_IRWXU)
 
             # scan the directory and populate self.images
-            total_used = 0
+            virtual_alloc = 0
+            physical_used = 0
             seen_images = []
-            for filename in os.listdir(XEND_STORAGE_DIR):
+            for filename in os.listdir(self.location):
                 if filename[-5:] == XEND_STORAGE_QCOW_FILENAME[-5:]:
                     image_uuid = filename[:-5]
                     seen_images.append(image_uuid)
+
+                    qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid
+                    cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid
+                    qcow_path = os.path.join(self.location, qcow_file)
+                    cfg_path = os.path.join(self.location, cfg_file)
+                    
+                    phys_size = os.stat(qcow_path).st_size
+                    virt_size = qcow_virtual_size(qcow_path)
                     
                     # add this image if we haven't seen it before
                     if image_uuid not in self.images:
-                        qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid
-                        cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid
-                        qcow_path = os.path.join(XEND_STORAGE_DIR, qcow_file)
-                        cfg_path = os.path.join(XEND_STORAGE_DIR, cfg_file)
-
-                        qcow_size = os.stat(qcow_path).st_size
-
-                        # TODO: no way to stat virtual size of qcow
                         vdi = XendQCOWVDI(image_uuid, self.uuid,
                                           qcow_path, cfg_path,
-                                          qcow_size, qcow_size) 
+                                          virt_size, phys_size)
                         
                         if cfg_path and os.path.exists(cfg_path):
                             vdi.load_config(cfg_path)
                         
                         self.images[image_uuid] = vdi
-                        total_used += qcow_size
+
+                    physical_used += phys_size
+                    virtual_alloc += virt_size
 
             # remove images that aren't valid
             for image_uuid in self.images.keys():
@@ -142,11 +164,14 @@ class XendStorageRepository:
                         pass
                     del self.images[image_uuid]
 
+            self.storage_alloc = virtual_alloc
+            self.storage_used = physical_used
+
             # 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
+            if self.storage_max == XEND_STORAGE_NO_MAXIMUM:
+                self.storage_free = self._get_free_space()
             else:
-                self.storage_free = self._get_free_space()
+                self.storage_free = self.storage_max - self.storage_alloc
                         
         finally:
             self.lock.release()
@@ -158,7 +183,7 @@ class XendStorageRepository:
 
         @rtype: int
         """
-        stfs = os.statvfs(self.storage_dir)
+        stfs = os.statvfs(self.location)
         return stfs.f_bavail * stfs.f_frsize
 
     def _has_space_available_for(self, size_bytes):
@@ -167,22 +192,19 @@ class XendStorageRepository:
 
         @rtype: bool
         """
-        if self.storage_max != -1:
-            return self.storage_free
+        if self.storage_max != XEND_STORAGE_NO_MAXIMUM:
+            return self.storage_free > size_bytes
         
         bytes_free = self._get_free_space()
-        try:
-            if size_bytes < bytes_free:
-                return True
-        except DeviceInvalidError:
-            pass
+        if size_bytes < bytes_free:
+            return True
         return False
 
     def _create_image_files(self, desired_size_bytes):
         """Create an image and return its assigned UUID.
 
-        @param desired_size_kb: Desired image size in KB.
-        @type  desired_size_kb: int
+        @param desired_size_bytes: Desired image size in bytes
+        @type  desired_size_bytes: int
         @rtype: string
         @return: uuid
 
@@ -194,7 +216,7 @@ class XendStorageRepository:
                 raise XendError("Not enough space")
 
             image_uuid = uuid.createString()
-            qcow_path = os.path.join(XEND_STORAGE_DIR,
+            qcow_path = os.path.join(self.location,
                                      XEND_STORAGE_QCOW_FILENAME % image_uuid)
             
             if qcow_path and os.path.exists(qcow_path):
@@ -268,10 +290,7 @@ class XendStorageRepository:
         """
         self.lock.acquire()
         try:
-            if self.storage_max != XEND_STORAGE_MAX_IGNORE:
-                return self.storage_max
-            else:
-                return self.free_space_bytes() + self.used_space_bytes()
+            return self.storage_max
         finally:
             self.lock.release()
             
@@ -315,7 +334,7 @@ class XendStorageRepository:
 
             # save configuration to file
             cfg_filename =  XEND_STORAGE_VDICFG_FILENAME % image_uuid
-            cfg_path = os.path.join(XEND_STORAGE_DIR, cfg_filename)
+            cfg_path = os.path.join(self.location, cfg_filename)
             image.save_config(cfg_path)
             
         except Exception, e:
@@ -327,10 +346,10 @@ class XendStorageRepository:
 
         return image_uuid
         
-    def xen_api_get_by_label(self, label):
-        self.lock.acquire()
-        try:
-            for image_uuid, val in self.images.values():
+    def xen_api_get_by_name_label(self, label):
+        self.lock.acquire()
+        try:
+            for image_uuid, val in self.images.items():
                 if val.name_label == label:
                     return image_uuid
             return None

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.