mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
bitkeeper revision 1.1159.1.191 (41597997cc5ZJzvh6XLLSIhJ9hLEnA)
Initial support for automatic management of non-phy block devices.
This commit is contained in:
1 parent
0182d31a2a
commit
e52d6a8915
8 files changed
+197
-65
No files matched your search
@@ -0,0 +1,42 @@
|
||||
"""Xend interface to block control scripts.
|
||||
"""
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import string
|
||||
|
||||
from xen.xend import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
|
||||
"""Where network control scripts live."""
|
||||
SCRIPT_DIR = xroot.block_script_dir
|
||||
|
||||
def block(op, type, dets, script=None):
|
||||
"""Call a block control script.
|
||||
Xend calls this with op 'bind' when it is about to export a block device
|
||||
(other than a raw partition). The script is called with unbind when a
|
||||
device is no longer in use and should be removed.
|
||||
|
||||
@param op: operation (start, stop, status)
|
||||
@param type: type of block device (determines the script used)
|
||||
@param dets: arguments to the control script
|
||||
@param script: block script name
|
||||
"""
|
||||
|
||||
if op not in ['bind', 'unbind']:
|
||||
raise ValueError('Invalid operation:' + op)
|
||||
|
||||
# Special case phy devices - they don't require any (un)binding
|
||||
if type == 'phy':
|
||||
return dets
|
||||
|
||||
if script is None:
|
||||
script = xroot.get_block_script(type)
|
||||
script = os.path.join(SCRIPT_DIR, script)
|
||||
args = [op] + string.split(dets, ':')
|
||||
args = ' '.join(args)
|
||||
out = os.popen(script + ' ' + args)
|
||||
|
||||
output = out.readline()
|
||||
out.close()
|
||||
return string.rstrip(output)
|
||||
@@ -33,6 +33,8 @@ 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.
|
||||
@@ -90,61 +92,6 @@ def shutdown_reason(code):
|
||||
"""
|
||||
return shutdown_reasons.get(code, "?")
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
def lookup_disk_uname(uname):
|
||||
"""Lookup a list of segments for a physical device.
|
||||
|
||||
@param uname: name of the device in the format \'phy:dev\' for a physical device
|
||||
@type uname: string
|
||||
@return: list of extents that make up the named device
|
||||
@rtype: [dict]
|
||||
"""
|
||||
( type, d_name ) = string.split( uname, ':' )
|
||||
|
||||
if type == "phy":
|
||||
segments = lookup_raw_partn( d_name )
|
||||
else:
|
||||
segments = None
|
||||
return segments
|
||||
|
||||
def make_disk(vm, config, uname, dev, mode, recreate=0):
|
||||
"""Create a virtual disk device for a domain.
|
||||
|
||||
@@ -156,18 +103,12 @@ def make_disk(vm, config, uname, dev, mode, recreate=0):
|
||||
@return: deferred
|
||||
"""
|
||||
idx = vm.next_device_index('vbd')
|
||||
segments = lookup_disk_uname(uname)
|
||||
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]
|
||||
# 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, vdev, mode, segment, 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.
|
||||
|
||||
@@ -46,6 +46,9 @@ class XendRoot:
|
||||
"""Where network control scripts live."""
|
||||
network_script_dir = "/etc/xen/scripts"
|
||||
|
||||
"""Where block control scripts live."""
|
||||
block_script_dir = "/etc/xen/scripts"
|
||||
|
||||
logfile_default = "/var/log/xend.log"
|
||||
|
||||
loglevel_default = 'DEBUG'
|
||||
@@ -260,6 +263,9 @@ class XendRoot:
|
||||
def get_xend_address(self):
|
||||
return self.get_config_value('xend-address', '')
|
||||
|
||||
def get_block_script(self, type):
|
||||
return self.get_config_value('block-%s' % type, '')
|
||||
|
||||
def get_network_script(self):
|
||||
return self.get_config_value('network-script', 'network')
|
||||
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
from twisted.internet import defer
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import Blkctl
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendError import XendError, VmError
|
||||
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import channel
|
||||
import controller
|
||||
from messages import *
|
||||
@@ -257,6 +261,11 @@ class BlkDev(controller.SplitDev):
|
||||
val.append(['uname', self.uname])
|
||||
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.
|
||||
|
||||
@@ -266,6 +275,7 @@ class BlkDev(controller.SplitDev):
|
||||
d = self.send_be_vbd_destroy()
|
||||
if change:
|
||||
d.addCallback(lambda val: self.interfaceChanged())
|
||||
d.addCallback(lambda val: self.unbind())
|
||||
|
||||
def interfaceChanged(self):
|
||||
"""Tell the back-end to notify the front-end that a device has been
|
||||
@@ -345,7 +355,47 @@ class BlkDev(controller.SplitDev):
|
||||
backend.writeRequest(msg, response=d)
|
||||
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.
|
||||
@@ -386,7 +436,7 @@ class BlkifController(controller.SplitController):
|
||||
self.devices[idx] = dev
|
||||
return dev
|
||||
|
||||
def attachDevice(self, idx, config, vdev, mode, segment, recreate=0):
|
||||
def attachDevice(self, idx, config, uname, vdev, mode, recreate=0):
|
||||
"""Attach a device to the specified interface.
|
||||
On success the returned deferred will be called with the device.
|
||||
|
||||
@@ -403,10 +453,30 @@ class BlkifController(controller.SplitController):
|
||||
@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)
|
||||
|
||||
if recreate:
|
||||
d = defer.succeed(dev)
|
||||
else:
|
||||
dev.node = node
|
||||
dev.type = type
|
||||
d = dev.attach()
|
||||
return d
|
||||
|
||||
|
||||