]> xenbits.xensource.com Git - xen.git/commitdiff
bitkeeper revision 1.1159.116.3 (4177deb5O2BSukkVC4mhGVTFnJJiwA)
authormjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Thu, 21 Oct 2004 16:07:17 +0000 (16:07 +0000)
committermjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Thu, 21 Oct 2004 16:07:17 +0000 (16:07 +0000)
Fix problem with block device recreation after xend restart.
Save device state info in the domain record and use when
the domain info and devices are recreated.

tools/python/xen/xend/XendDomainInfo.py
tools/python/xen/xend/server/blkif.py
tools/python/xen/xend/server/controller.py
tools/python/xen/xend/server/netif.py

index 8cf30ffd2518e97300289d89d55ee86310dd0403..472711ab115f7b3184e199d923c147455c52ec7d 100644 (file)
@@ -33,8 +33,6 @@ xend = server.SrvDaemon.instance()
 
 from XendError import VmError
 
-from server.blkif import blkdev_name_to_number
-
 """The length of domain names that Xen can handle.
 The names stored in Xen itself are not used for much, and
 xend can handle domain names of any length.
@@ -92,24 +90,6 @@ def shutdown_reason(code):
     """
     return shutdown_reasons.get(code, "?")
 
-def make_disk(vm, config, uname, dev, mode, recreate=0):
-    """Create a virtual disk device for a domain.
-
-    @param vm:       vm
-    @param uname:    device to export
-    @param dev:      device name in domain
-    @param mode:     read/write mode
-    @param recreate: recreate flag (after xend restart)
-    @return: deferred
-    """
-    idx = vm.next_device_index('vbd')
-    # todo: The 'dev' should be looked up in the context of the domain.
-    vdev = blkdev_name_to_number(dev)
-    if not vdev:
-        raise VmError("vbd: Device not found: uname=%s dev=%s" % (uname, dev))
-    ctrl = xend.blkif_create(vm.dom, recreate=recreate)
-    return ctrl.attachDevice(idx, config, uname, vdev, mode, recreate=recreate)
-        
 def vif_up(iplist):
     """send an unsolicited ARP reply for all non link-local IP addresses.
 
@@ -222,6 +202,7 @@ def vm_recreate(savedinfo, info):
     """
     vm = XendDomainInfo()
     vm.recreate = 1
+    vm.savedinfo = savedinfo
     vm.setdom(info['dom'])
     #vm.name = info['name']
     vm.memory = info['mem_kb']/1024
@@ -239,6 +220,7 @@ def vm_recreate(savedinfo, info):
         vm.name = info['name']
         d = defer.succeed(vm)
     vm.recreate = 0
+    vm.savedinfo = None
     return d
 
 def vm_restore(src, progress=0):
@@ -336,6 +318,7 @@ class XendDomainInfo:
         self.restart_state = None
         self.restart_time = None
         self.console_port = None
+        self.savedinfo = None
 
     def setdom(self, dom):
         """Set the domain id.
@@ -397,10 +380,21 @@ class XendDomainInfo:
             sxpr.append(['restart_state', self.restart_state])
         if self.restart_time:
             sxpr.append(['restart_time', str(self.restart_time)])
+        devs = self.sxpr_devices()
+        if devs:
+            sxpr.append(devs)
         if self.config:
             sxpr.append(['config', self.config])
         return sxpr
 
+    def sxpr_devices(self):
+        sxpr = ['devices']
+        for devs in self.devices.values():
+            for dev in devs:
+                if hasattr(dev, 'sxpr'):
+                    sxpr.append(dev.sxpr())
+        return sxpr
+
     def check_name(self, name):
         """Check if a vm name is valid. Valid names start with a non-digit
         and contain alphabetic characters, digits, or characters in '_-.:/+'.
@@ -586,6 +580,25 @@ class XendDomainInfo:
                 return d
         return None
 
+    def get_device_savedinfo(self, type, index):
+        val = None
+        if self.savedinfo is None:
+            return val
+        index = str(index)
+        devinfo = sxp.child(self.savedinfo, 'devices')
+        if devinfo is None:
+            return val
+        for d in sxp.children(devinfo, type):
+            dindex = sxp.child_value(d, 'index')
+            if dindex is None: continue
+            if str(dindex) == index:
+                val = d
+                break
+        return val
+
+    def get_device_recreate(self, type, index):
+        return self.get_device_savedinfo(type, index) or self.recreate
+
     def add_config(self, val):
         """Add configuration data to a virtual machine.
 
@@ -840,7 +853,7 @@ class XendDomainInfo:
     def configure_memory(self):
         """Configure vm memory limit.
         """
-        maxmem = sxp.get_child_value(self.config, "maxmem")
+        maxmem = sxp.child_value(self.config, "maxmem")
         if maxmem is None:
             maxmem = self.memory
         xc.domain_setmaxmem(self.dom, maxmem_kb = maxmem * 1024)
@@ -1070,9 +1083,11 @@ def vm_dev_vif(vm, val, index, change=0):
     vmac = sxp.child_value(val, "mac")
     ctrl = xend.netif_create(vm.dom, recreate=vm.recreate)
     log.debug("Creating vif dom=%d vif=%d mac=%s", vm.dom, vif, str(vmac))
-    defer = ctrl.attachDevice(vif, val, recreate=vm.recreate)
+    recreate = vm.get_device_recreate('vif', index)
+    defer = ctrl.attachDevice(vif, val, recreate=recreate)
     def cbok(dev):
         dev.vifctl('up', vmname=vm.name)
+        dev.setIndex(index)
         vm.add_device('vif', dev)
         if change:
             dev.interfaceChanged()
@@ -1088,23 +1103,19 @@ def vm_dev_vbd(vm, val, index, change=0):
     @param index:     vbd index
     @return: deferred
     """
+    idx = vm.next_device_index('vbd')
     uname = sxp.child_value(val, 'uname')
-    if not uname:
-        raise VmError('vbd: Missing uname')
-    dev = sxp.child_value(val, 'dev')
-    if not dev:
-        raise VmError('vbd: Missing dev')
-    mode = sxp.child_value(val, 'mode', 'r')
-    log.debug("Creating vbd dom=%d uname=%s dev=%s", vm.dom, uname, dev)
-    defer = make_disk(vm, val, uname, dev, mode, vm.recreate)
-    def fn(vbd):
-        vbd.dev = dev
-        vbd.uname = uname
-        vm.add_device('vbd', vbd)
+    log.debug("Creating vbd dom=%d uname=%s", vm.dom, uname)
+    ctrl = xend.blkif_create(vm.dom, recreate=vm.recreate)
+    recreate = vm.get_device_recreate('vbd', index)
+    defer = ctrl.attachDevice(idx, val, recreate=recreate)
+    def cbok(dev):
+        dev.setIndex(index)
+        vm.add_device('vbd', dev)
         if change:
-            vbd.interfaceChanged()
-        return vbd
-    defer.addCallback(fn)
+            dev.interfaceChanged()
+        return dev
+    defer.addCallback(cbok)
     return defer
 
 def parse_pci(val):
index e2ba4af4ea51ea5307792e91afae7eab3090f7c5..79998299cba9d7551d97e7bea16075681930fc5c 100755 (executable)
@@ -16,6 +16,46 @@ import channel
 import controller
 from messages import *
 
+
+def blkdev_name_to_number(name):
+    """Take the given textual block-device name (e.g., '/dev/sda1',
+    'hda') and return the device number used by the OS. """
+
+    if re.match( '^/dev/', name ):
+       n = name
+    else:
+       n = '/dev/' + name
+    
+    try:
+       return os.stat(n).st_rdev
+    except Exception, ex:
+        log.debug("exception looking up device number for %s: %s", name, ex)
+       pass
+
+    # see if this is a hex device number
+    if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
+       return string.atoi(name,16)
+       
+    return None
+
+def blkdev_segment(name):
+    """Take the given block-device name (e.g. '/dev/sda1', 'hda')
+    and return a dictionary { device, start_sector,
+    nr_sectors, type }
+        device:       Device number of the given partition
+        start_sector: Index of first sector of the partition
+        nr_sectors:   Number of sectors comprising this partition
+        type:         'Disk' or identifying name for partition type
+    """
+    val = None
+    n = blkdev_name_to_number(name)
+    if n:
+       val = { 'device' : n,
+                'start_sector' : long(0),
+                'nr_sectors' : long(1L<<63),
+                'type' : 'Disk' }
+    return val
+
 class BlkifBackendController(controller.BackendController):
     """ Handler for the 'back-end' channel to a block device driver domain.
     """
@@ -231,26 +271,70 @@ class BlkDev(controller.SplitDev):
     """Info record for a block device.
     """
 
-    def __init__(self, idx, ctrl, config, vdev, mode, segment):
+    def __init__(self, idx, ctrl, config):
         controller.SplitDev.__init__(self, idx, ctrl)
-        self.config = config
         self.dev = None
         self.uname = None
-        self.vdev = vdev
-        self.mode = mode
-        self.device = segment['device']
-        self.start_sector = segment['start_sector']
-        self.nr_sectors = segment['nr_sectors']
+        self.vdev = None
+        self.mode = None
+        self.type = None
+        self.params = None
+        self.node = None
+        self.device = None
+        self.start_sector = None
+        self.nr_sectors = None
+        self.configure(config)
+
+    def configure(self, config):
+        self.config = config
+        self.uname = sxp.child_value(config, 'uname')
+        if not self.uname:
+            raise VmError('vbd: Missing uname')
+        # Split into type and type-specific params (which are passed to the
+        # type-specific control script).
+        (self.type, self.params) = string.split(self.uname, ':', 1)
+        self.dev = sxp.child_value(config, 'dev')
+        if not self.dev:
+            raise VmError('vbd: Missing dev')
+        self.mode = sxp.child_value(config, 'mode', 'r')
+        # todo: The 'dev' should be looked up in the context of the domain.
+        self.vdev = blkdev_name_to_number(self.dev)
+        if not self.vdev:
+            raise VmError('vbd: Device not found: %s' % self.dev)
         try:
             self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
         except:
             raise XendError('invalid backend domain')
 
+    def recreate(self, savedinfo):
+        node = sxp.child_value(savedinfo, 'node')
+        self.setNode(node)
+
+    def attach(self):
+        node = Blkctl.block('bind', self.type, self.params)
+        self.setNode(node)
+        return self.attachBackend()
+
+    def unbind(self):
+        if self.node is None: return
+        log.debug("Unbinding vbd (type %s) from %s"
+                  % (self.type, self.node))
+        Blkctl.block('unbind', self.type, self.node)
+
+    def setNode(self, node):
+        segment = blkdev_segment(node)
+        if not segment:
+            raise VmError("vbd: Segment not found: uname=%s" % self.uname)
+        self.node = node
+        self.device = segment['device']
+        self.start_sector = segment['start_sector']
+        self.nr_sectors = segment['nr_sectors']
+
     def readonly(self):
         return 'w' not in self.mode
 
     def sxpr(self):
-        val = ['blkdev',
+        val = ['vbd',
                ['idx', self.idx],
                ['vdev', self.vdev],
                ['device', self.device],
@@ -259,13 +343,12 @@ class BlkDev(controller.SplitDev):
             val.append(['dev', self.dev])
         if self.uname:
             val.append(['uname', self.uname])
+        if self.node:
+            val.append(['node', self.node])
+        if self.index is not None:
+            val.append(['index', self.index])
         return val
 
-    def unbind(self):
-        log.debug("Unbinding block dev (type %s) from %s"
-                  % (self.type, self.node))
-        Blkctl.block('unbind', self.type, self.node)
-
     def destroy(self, change=0):
         """Destroy the device. If 'change' is true notify the front-end interface.
 
@@ -283,7 +366,7 @@ class BlkDev(controller.SplitDev):
         """
         self.getBackendInterface().interfaceChanged()
 
-    def attach(self):
+    def attachBackend(self):
         """Attach the device to its controller.
 
         """
@@ -356,46 +439,6 @@ class BlkDev(controller.SplitDev):
         return d
         
 
-def blkdev_name_to_number(name):
-    """Take the given textual block-device name (e.g., '/dev/sda1',
-    'hda') and return the device number used by the OS. """
-
-    if not re.match( '^/dev/', name ):
-       n = '/dev/' + name
-    else:
-       n = name
-    
-    try:
-       return os.stat(n).st_rdev
-    except Exception, e:
-        print "blkdev_name_to_number> exception looking up device number for %s: %s" % (name, e)
-       pass
-
-    # see if this is a hex device number
-    if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
-       return string.atoi(name,16)
-       
-    return None
-
-def lookup_raw_partn(name):
-    """Take the given block-device name (e.g., '/dev/sda1', 'hda')
-    and return a dictionary { device, start_sector,
-    nr_sectors, type }
-        device:       Device number of the given partition
-        start_sector: Index of first sector of the partition
-        nr_sectors:   Number of sectors comprising this partition
-        type:         'Disk' or identifying name for partition type
-    """
-
-    n = blkdev_name_to_number(name)
-    if n:
-       return [ { 'device' : n,
-                  'start_sector' : long(0),
-                  'nr_sectors' : long(1L<<63),
-                  'type' : 'Disk' } ]
-    else:
-       return None
-
 class BlkifController(controller.SplitController):
     """Block device interface controller. Handles all block devices
     for a domain.
@@ -418,65 +461,37 @@ class BlkifController(controller.SplitController):
         val = ['blkif', ['dom', self.dom]]
         return val
 
-    def addDevice(self, idx, config, vdev, mode, segment):
+    def addDevice(self, idx, config):
         """Add a device to the device table.
 
         @param vdev:     device index
         @type  vdev:     int
-        @param mode:     read/write mode
-        @type  mode:     string
-        @param segment:  segment
-        @type  segment:  int
+        @param config: device configuration
         @return: device
         @rtype:  BlkDev
         """
         if idx in self.devices:
             raise XendError('device exists: ' + str(idx))
-        dev = BlkDev(idx, self, config, vdev, mode, segment)
+        dev = BlkDev(idx, self, config )
         self.devices[idx] = dev
         return dev
 
-    def attachDevice(self, idx, config, uname, vdev, mode, recreate=0):
+    def attachDevice(self, idx, config, recreate=0):
         """Attach a device to the specified interface.
         On success the returned deferred will be called with the device.
 
         @param idx:      device id
         @param config:   device configuration
-        @param vdev:     device index
-        @type  vdev:     int
-        @param mode:     read/write mode
-        @type  mode:     string
-        @param segment:  segment
-        @type  segment:  int
         @param recreate: if true it's being recreated (after xend restart)
         @type  recreate: bool
         @return: deferred
         @rtype:  Deferred
         """
-        if not recreate:
-            # Split into type and type-specific details (which are passed to the
-            # type-specific control script).
-            type, dets = string.split(uname, ':', 1)
-            # Special case: don't bother calling a script for phy.  Could
-            # alternatively provide a "do nothing" script for phy devices...
-            node = Blkctl.block('bind', type, dets)
-
-        segments = lookup_raw_partn(node)
-
-        if not segments:
-            raise VmError("vbd: Segments not found: uname=%s" % uname)
-        if len(segments) > 1:
-            raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
-
-        segment = segments[0]            
-
-        dev = self.addDevice(idx, config, vdev, mode, segment)
-            
+        dev = self.addDevice(idx, config)
         if recreate:
+            dev.recreate(recreate)
             d = defer.succeed(dev)
         else:
-            dev.node = node
-            dev.type = type
             d = dev.attach()
         return d
 
index 3721b9b3b3349214ee71fcf095ce23c14ee36313..c8962d467592919c83cd61147440851826a47484 100755 (executable)
@@ -682,10 +682,17 @@ class SplitDev(Dev):
     def __init__(self, idx, controller):
         Dev.__init__(self, idx, controller)
         self.backendDomain = 0
+        self.index = None
 
     def getBackendInterface(self):
         return self.controller.getBackendInterface(self.backendDomain)
 
+    def getIndex(self):
+        return self.index
+
+    def setIndex(self, index):
+        self.index = index
+
 
 
     
index 38e722347aaeb9bed475b6e220b351993de29938..a0466b0e4cdf89af4b21f0ada6e839eeb0b14d68 100755 (executable)
@@ -198,6 +198,8 @@ class NetDev(controller.SplitDev):
             val.append(['evtchn',
                         self.evtchn['port1'],
                         self.evtchn['port2']])
+        if self.index is not None:
+            val.append(['index', self.index])
         return val
 
     def get_vifname(self):