direct-io.hg

view tools/python/xen/xend/server/blkif.py @ 2609:03d1ce22b0b2

bitkeeper revision 1.1159.103.2 (4163f8fdRWsu23t9HYw_MCuUZuSSPA)

Make vbd error message more informative.
author mjw@wray-m-3.hpl.hp.com
date Wed Oct 06 13:54:05 2004 +0000 (2004-10-06)
parents 82cab392896d
children 1314598e0d84
line source
1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
2 """Support for virtual block devices.
3 """
5 from twisted.internet import defer
7 from xen.xend import sxp
8 from xen.xend import Blkctl
9 from xen.xend.XendLogging import log
10 from xen.xend.XendError import XendError, VmError
12 import os
13 import re
14 import string
15 import channel
16 import controller
17 from messages import *
19 class BlkifBackendController(controller.BackendController):
20 """ Handler for the 'back-end' channel to a block device driver domain.
21 """
23 def __init__(self, factory, dom):
24 controller.BackendController.__init__(self, factory, dom)
25 self.addMethod(CMSG_BLKIF_BE,
26 CMSG_BLKIF_BE_DRIVER_STATUS,
27 self.recv_be_driver_status)
28 self.registerChannel()
30 def recv_be_driver_status(self, msg, req):
31 """Request handler for be_driver_status messages.
33 @param msg: message
34 @type msg: xu message
35 @param req: request flag (true if the msg is a request)
36 @type req: bool
37 """
38 val = unpackMsg('blkif_be_driver_status_t', msg)
39 status = val['status']
41 class BlkifBackendInterface(controller.BackendInterface):
42 """ Handler for the 'back-end' channel to a block device driver domain
43 on behalf of a front-end domain.
44 Must be connected using connect() before it can be used.
45 Do not create directly - use getBackendInterface() on the BlkifController.
46 """
48 def __init__(self, ctrl, dom, handle):
49 controller.BackendInterface.__init__(self, ctrl, dom, handle)
50 self.connected = 0
51 self.evtchn = None
52 self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
54 def __str__(self):
55 return '<BlkifBackendInterface %d %d>' % (self.controller.dom, self.dom)
57 def getEventChannelBackend(self):
58 val = 0
59 if self.evtchn:
60 val = self.evtchn['port1']
61 return val
63 def getEventChannelFrontend(self):
64 val = 0
65 if self.evtchn:
66 val = self.evtchn['port2']
67 return val
69 def connect(self, recreate=0):
70 """Connect to the blkif control interface.
72 @param recreate: true if after xend restart
73 @return: deferred
74 """
75 log.debug("Connecting blkif %s", str(self))
76 if recreate or self.connected:
77 d = defer.succeed(self)
78 else:
79 d = self.send_be_create()
80 d.addCallback(self.respond_be_create)
81 return d
83 def send_be_create(self):
84 d = defer.Deferred()
85 msg = packMsg('blkif_be_create_t',
86 { 'domid' : self.controller.dom,
87 'blkif_handle' : self.handle })
88 self.writeRequest(msg, response=d)
89 return d
91 def respond_be_create(self, msg):
92 val = unpackMsg('blkif_be_create_t', msg)
93 self.connected = 1
94 return self
96 def destroy(self):
97 """Disconnect from the blkif control interface and destroy it.
98 """
99 def cb_destroy(val):
100 self.send_be_destroy()
101 self.close()
102 d = defer.Deferred()
103 d.addCallback(cb_destroy)
104 self.send_be_disconnect(response=d)
106 def send_be_disconnect(self, response=None):
107 msg = packMsg('blkif_be_disconnect_t',
108 { 'domid' : self.controller.dom,
109 'blkif_handle' : self.handle })
110 self.writeRequest(msg, response=response)
112 def send_be_destroy(self, response=None):
113 msg = packMsg('blkif_be_destroy_t',
114 { 'domid' : self.controller.dom,
115 'blkif_handle' : self.handle })
116 self.writeRequest(msg, response=response)
118 def connectInterface(self, val):
119 self.evtchn = channel.eventChannel(0, self.controller.dom)
120 log.debug("Connecting blkif to event channel %s ports=%d:%d",
121 str(self), self.evtchn['port1'], self.evtchn['port2'])
122 msg = packMsg('blkif_be_connect_t',
123 { 'domid' : self.controller.dom,
124 'blkif_handle' : self.handle,
125 'evtchn' : self.getEventChannelBackend(),
126 'shmem_frame' : val['shmem_frame'] })
127 d = defer.Deferred()
128 d.addCallback(self.respond_be_connect)
129 self.writeRequest(msg, response=d)
131 def respond_be_connect(self, msg):
132 """Response handler for a be_connect message.
134 @param msg: message
135 @type msg: xu message
136 """
137 val = unpackMsg('blkif_be_connect_t', msg)
138 self.status = BLKIF_INTERFACE_STATUS_CONNECTED
139 self.send_fe_interface_status()
141 def send_fe_interface_status(self, response=None):
142 msg = packMsg('blkif_fe_interface_status_t',
143 { 'handle' : self.handle,
144 'status' : self.status,
145 'domid' : self.dom,
146 'evtchn' : self.getEventChannelFrontend() })
147 self.controller.writeRequest(msg, response=response)
149 def interfaceDisconnected(self):
150 self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
151 #todo?: Do this: self.evtchn = None
152 self.send_fe_interface_status()
154 def interfaceChanged(self):
155 """Notify the front-end that devices have been added or removed.
156 The front-end should then probe for devices.
157 """
158 msg = packMsg('blkif_fe_interface_status_t',
159 { 'handle' : self.handle,
160 'status' : BLKIF_INTERFACE_STATUS_CHANGED,
161 'domid' : self.dom,
162 'evtchn' : 0 })
163 self.controller.writeRequest(msg)
165 class BlkifControllerFactory(controller.SplitControllerFactory):
166 """Factory for creating block device interface controllers.
167 """
169 def __init__(self):
170 controller.SplitControllerFactory.__init__(self)
172 def createController(self, dom, recreate=0):
173 """Create a block device controller for a domain.
175 @param dom: domain
176 @type dom: int
177 @param recreate: if true it's a recreate (after xend restart)
178 @type recreate: bool
179 @return: block device controller
180 @rtype: BlkifController
181 """
182 blkif = self.getControllerByDom(dom)
183 if blkif is None:
184 blkif = BlkifController(self, dom)
185 self.addController(blkif)
186 return blkif
188 def createBackendController(self, dom):
189 """Create a block device backend controller.
191 @param dom: backend domain
192 @return: backend controller
193 """
194 return BlkifBackendController(self, dom)
196 def createBackendInterface(self, ctrl, dom, handle):
197 """Create a block device backend interface.
199 @param ctrl: controller
200 @param dom: backend domain
201 @param handle: interface handle
202 @return: backend interface
203 """
204 return BlkifBackendInterface(ctrl, dom, handle)
206 def getDomainDevices(self, dom):
207 """Get the block devices for a domain.
209 @param dom: domain
210 @type dom: int
211 @return: devices
212 @rtype: [device]
213 """
214 blkif = self.getControllerByDom(dom)
215 return (blkif and blkif.getDevices()) or []
217 def getDomainDevice(self, dom, idx):
218 """Get a block device from a domain.
220 @param dom: domain
221 @type dom: int
222 @param idx: device index
223 @type idx: int
224 @return: device
225 @rtype: device
226 """
227 blkif = self.getControllerByDom(dom)
228 return (blkif and blkif.getDevice(idx)) or None
230 class BlkDev(controller.SplitDev):
231 """Info record for a block device.
232 """
234 def __init__(self, idx, ctrl, config, vdev, mode, segment):
235 controller.SplitDev.__init__(self, idx, ctrl)
236 self.config = config
237 self.dev = None
238 self.uname = None
239 self.vdev = vdev
240 self.mode = mode
241 self.device = segment['device']
242 self.start_sector = segment['start_sector']
243 self.nr_sectors = segment['nr_sectors']
244 try:
245 self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
246 except:
247 raise XendError('invalid backend domain')
249 def readonly(self):
250 return 'w' not in self.mode
252 def sxpr(self):
253 val = ['blkdev',
254 ['idx', self.idx],
255 ['vdev', self.vdev],
256 ['device', self.device],
257 ['mode', self.mode]]
258 if self.dev:
259 val.append(['dev', self.dev])
260 if self.uname:
261 val.append(['uname', self.uname])
262 return val
264 def unbind(self):
265 log.debug("Unbinding block dev (type %s) from %s"
266 % (self.type, self.node))
267 Blkctl.block('unbind', self.type, self.node)
269 def destroy(self, change=0):
270 """Destroy the device. If 'change' is true notify the front-end interface.
272 @param change: change flag
273 """
274 log.debug("Destroying vbd domain=%d idx=%s", self.controller.dom, self.idx)
275 d = self.send_be_vbd_destroy()
276 if change:
277 d.addCallback(lambda val: self.interfaceChanged())
278 d.addCallback(lambda val: self.unbind())
280 def interfaceChanged(self):
281 """Tell the back-end to notify the front-end that a device has been
282 added or removed.
283 """
284 self.getBackendInterface().interfaceChanged()
286 def attach(self):
287 """Attach the device to its controller.
289 """
290 backend = self.getBackendInterface()
291 d1 = backend.connect()
292 d2 = defer.Deferred()
293 d2.addCallback(self.send_be_vbd_create)
294 d1.chainDeferred(d2)
295 return d2
297 def send_be_vbd_create(self, val):
298 d = defer.Deferred()
299 d.addCallback(self.respond_be_vbd_create)
300 backend = self.getBackendInterface()
301 msg = packMsg('blkif_be_vbd_create_t',
302 { 'domid' : self.controller.dom,
303 'blkif_handle' : backend.handle,
304 'vdevice' : self.vdev,
305 'readonly' : self.readonly() })
306 backend.writeRequest(msg, response=d)
307 return d
309 def respond_be_vbd_create(self, msg):
310 """Response handler for a be_vbd_create message.
311 Tries to grow the vbd.
313 @param msg: message
314 @type msg: xu message
315 """
316 val = unpackMsg('blkif_be_vbd_create_t', msg)
317 d = self.send_be_vbd_grow()
318 d.addCallback(self.respond_be_vbd_grow)
319 return d
321 def send_be_vbd_grow(self):
322 d = defer.Deferred()
323 backend = self.getBackendInterface()
324 msg = packMsg('blkif_be_vbd_grow_t',
325 { 'domid' : self.controller.dom,
326 'blkif_handle' : backend.handle,
327 'vdevice' : self.vdev,
328 'extent.device' : self.device,
329 'extent.sector_start' : self.start_sector,
330 'extent.sector_length' : self.nr_sectors })
331 backend.writeRequest(msg, response=d)
332 return d
334 def respond_be_vbd_grow(self, msg):
335 """Response handler for a be_vbd_grow message.
337 @param msg: message
338 @type msg: xu message
339 """
340 val = unpackMsg('blkif_be_vbd_grow_t', msg)
341 status = val['status']
342 if status != BLKIF_BE_STATUS_OKAY:
343 raise XendError("Adding extent to vbd failed: device %s, error %d"
344 % (sxp.to_string(self.config), status))
345 return self
347 def send_be_vbd_destroy(self):
348 d = defer.Deferred()
349 backend = self.getBackendInterface()
350 msg = packMsg('blkif_be_vbd_destroy_t',
351 { 'domid' : self.controller.dom,
352 'blkif_handle' : backend.handle,
353 'vdevice' : self.vdev })
354 self.controller.delDevice(self.vdev)
355 backend.writeRequest(msg, response=d)
356 return d
359 def blkdev_name_to_number(name):
360 """Take the given textual block-device name (e.g., '/dev/sda1',
361 'hda') and return the device number used by the OS. """
363 if not re.match( '^/dev/', name ):
364 n = '/dev/' + name
365 else:
366 n = name
368 try:
369 return os.stat(n).st_rdev
370 except Exception, e:
371 print "blkdev_name_to_number> exception looking up device number for %s: %s" % (name, e)
372 pass
374 # see if this is a hex device number
375 if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
376 return string.atoi(name,16)
378 return None
380 def lookup_raw_partn(name):
381 """Take the given block-device name (e.g., '/dev/sda1', 'hda')
382 and return a dictionary { device, start_sector,
383 nr_sectors, type }
384 device: Device number of the given partition
385 start_sector: Index of first sector of the partition
386 nr_sectors: Number of sectors comprising this partition
387 type: 'Disk' or identifying name for partition type
388 """
390 n = blkdev_name_to_number(name)
391 if n:
392 return [ { 'device' : n,
393 'start_sector' : long(0),
394 'nr_sectors' : long(1L<<63),
395 'type' : 'Disk' } ]
396 else:
397 return None
399 class BlkifController(controller.SplitController):
400 """Block device interface controller. Handles all block devices
401 for a domain.
402 """
404 def __init__(self, factory, dom):
405 """Create a block device controller.
406 Do not call directly - use createController() on the factory instead.
407 """
408 controller.SplitController.__init__(self, factory, dom)
409 self.addMethod(CMSG_BLKIF_FE,
410 CMSG_BLKIF_FE_DRIVER_STATUS,
411 self.recv_fe_driver_status)
412 self.addMethod(CMSG_BLKIF_FE,
413 CMSG_BLKIF_FE_INTERFACE_CONNECT,
414 self.recv_fe_interface_connect)
415 self.registerChannel()
417 def sxpr(self):
418 val = ['blkif', ['dom', self.dom]]
419 return val
421 def addDevice(self, idx, config, vdev, mode, segment):
422 """Add a device to the device table.
424 @param vdev: device index
425 @type vdev: int
426 @param mode: read/write mode
427 @type mode: string
428 @param segment: segment
429 @type segment: int
430 @return: device
431 @rtype: BlkDev
432 """
433 if idx in self.devices:
434 raise XendError('device exists: ' + str(idx))
435 dev = BlkDev(idx, self, config, vdev, mode, segment)
436 self.devices[idx] = dev
437 return dev
439 def attachDevice(self, idx, config, uname, vdev, mode, recreate=0):
440 """Attach a device to the specified interface.
441 On success the returned deferred will be called with the device.
443 @param idx: device id
444 @param config: device configuration
445 @param vdev: device index
446 @type vdev: int
447 @param mode: read/write mode
448 @type mode: string
449 @param segment: segment
450 @type segment: int
451 @param recreate: if true it's being recreated (after xend restart)
452 @type recreate: bool
453 @return: deferred
454 @rtype: Deferred
455 """
456 if not recreate:
457 # Split into type and type-specific details (which are passed to the
458 # type-specific control script).
459 type, dets = string.split(uname, ':', 1)
460 # Special case: don't bother calling a script for phy. Could
461 # alternatively provide a "do nothing" script for phy devices...
462 node = Blkctl.block('bind', type, dets)
464 segments = lookup_raw_partn(node)
466 if not segments:
467 raise VmError("vbd: Segments not found: uname=%s" % uname)
468 if len(segments) > 1:
469 raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
471 segment = segments[0]
473 dev = self.addDevice(idx, config, vdev, mode, segment)
475 if recreate:
476 d = defer.succeed(dev)
477 else:
478 dev.node = node
479 dev.type = type
480 d = dev.attach()
481 return d
483 def destroy(self):
484 """Destroy the controller and all devices.
485 """
486 log.debug("Destroying blkif domain=%d", self.dom)
487 self.destroyDevices()
488 self.destroyBackends()
490 def destroyDevices(self):
491 """Destroy all devices.
492 """
493 for dev in self.getDevices():
494 dev.destroy()
496 def destroyBackends(self):
497 for backend in self.getBackendInterfaces():
498 backend.destroy()
500 def recv_fe_driver_status(self, msg, req):
501 val = unpackMsg('blkif_fe_driver_status_t', msg)
502 print 'recv_fe_driver_status>', val
503 for backend in self.getBackendInterfaces():
504 backend.interfaceDisconnected()
506 def recv_fe_interface_connect(self, msg, req):
507 val = unpackMsg('blkif_fe_interface_connect_t', msg)
508 handle = val['handle']
509 backend = self.getBackendInterfaceByHandle(handle)
510 if backend:
511 backend.connectInterface(val)
512 else:
513 log.error('interface connect on unknown interface: handle=%d', handle)