debuggers.hg

view tools/python/xen/xend/server/blkif.py @ 4655:a838a908e38e

bitkeeper revision 1.1327.2.2 (4267a9b3MhPpljnjQ5IbfLdzcW2K3w)

Remove twisted from the HTTP server and replace with a
threaded server. Add classes to provide tcp and unix servers
using threads instead of twisted. Remove use of twisted from
the consoles, event server and HTTP resources

Signed-off-by: Mike Wray <mike.wray@hp.com>
author mjw@wray-m-3.hpl.hp.com
date Thu Apr 21 13:25:07 2005 +0000 (2005-04-21)
parents c69fbe48a357
children b720e2fb6b00
line source
1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
2 """Support for virtual block devices.
3 """
5 import os
6 import re
7 import string
9 from xen.xend.XendError import XendError, VmError
10 from xen.xend import XendRoot
11 from xen.xend.XendLogging import log
12 from xen.xend import sxp
13 from xen.xend import Blkctl
15 import channel
16 from controller import CtrlMsgRcvr, Dev, DevController
17 from messages import *
19 from xen.util.ip import _readline, _readlines
21 def expand_dev_name(name):
22 if not name:
23 return name
24 if re.match( '^/dev/', name ):
25 return name
26 else:
27 return '/dev/' + name
29 def blkdev_name_to_number(name):
30 """Take the given textual block-device name (e.g., '/dev/sda1',
31 'hda') and return the device number used by the OS. """
33 n = expand_dev_name(name)
35 try:
36 return os.stat(n).st_rdev
37 except Exception, ex:
38 log.debug("exception looking up device number for %s: %s", name, ex)
39 pass
41 if re.match( '/dev/sd[a-p]([0-9]|1[0-5])', n):
42 return 8 * 256 + 16 * (ord(n[7:8]) - ord('a')) + int(n[8:])
44 if re.match( '/dev/hd[a-t]([1-9]|[1-5][0-9]|6[0-3])?', n):
45 ide_majors = [ 3, 22, 33, 34, 56, 57, 88, 89, 90, 91 ]
46 major = ide_majors[(ord(n[7:8]) - ord('a')) / 2]
47 minor = ((ord(n[7:8]) - ord('a')) % 2) * 64 + int(n[8:] or 0)
48 return major * 256 + minor
50 # see if this is a hex device number
51 if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
52 return string.atoi(name,16)
54 return None
56 def blkdev_segment(name):
57 """Take the given block-device name (e.g. '/dev/sda1', 'hda')
58 and return a dictionary { device, start_sector,
59 nr_sectors, type }
60 device: Device number of the given partition
61 start_sector: Index of first sector of the partition
62 nr_sectors: Number of sectors comprising this partition
63 type: 'Disk' or identifying name for partition type
64 """
65 val = None
66 n = blkdev_name_to_number(name)
67 if n:
68 val = { 'device' : n,
69 'start_sector' : long(0),
70 'nr_sectors' : long(1L<<63),
71 'type' : 'Disk' }
72 return val
74 def mount_mode(name):
75 mode = None
76 name = expand_dev_name(name)
77 lines = _readlines(os.popen('mount 2>/dev/null'))
78 exp = re.compile('^' + name + ' .*[\(,]r(?P<mode>[ow])[,\)]')
79 for line in lines:
80 pm = exp.match(line)
81 if not pm: continue
82 mode = pm.group('mode')
83 break
84 if mode == 'w':
85 return mode
86 if mode == 'o':
87 mode = 'r'
88 return mode
90 class BlkifBackend:
91 """ Handler for the 'back-end' channel to a block device driver domain
92 on behalf of a front-end domain.
93 Must be connected using connect() before it can be used.
94 """
96 def __init__(self, controller, id, dom, recreate=False):
97 self.controller = controller
98 self.id = id
99 self.frontendDomain = self.controller.getDomain()
100 self.frontendChannel = None
101 self.backendDomain = dom
102 self.backendChannel = None
103 self.destroyed = False
104 self.connected = False
105 self.evtchn = None
106 self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
108 def init(self, recreate=False, reboot=False):
109 self.destroyed = False
110 self.frontendDomain = self.controller.getDomain()
111 self.frontendChannel = self.controller.getChannel()
112 cf = channel.channelFactory()
113 self.backendChannel = cf.openChannel(self.backendDomain)
115 def __str__(self):
116 return ('<BlkifBackend frontend=%d backend=%d id=%d>'
117 % (self.frontendDomain,
118 self.backendDomain,
119 self.id))
121 def getId(self):
122 return self.id
124 def closeEvtchn(self):
125 if self.evtchn:
126 channel.eventChannelClose(self.evtchn)
127 self.evtchn = None
129 def openEvtchn(self):
130 self.evtchn = channel.eventChannel(self.backendDomain, self.frontendDomain)
132 def getEventChannelBackend(self):
133 val = 0
134 if self.evtchn:
135 val = self.evtchn['port1']
136 return val
138 def getEventChannelFrontend(self):
139 val = 0
140 if self.evtchn:
141 val = self.evtchn['port2']
142 return val
144 def connect(self, recreate=False):
145 """Connect to the blkif control interface.
147 @param recreate: true if after xend restart
148 """
149 log.debug("Connecting blkif %s", str(self))
150 if recreate or self.connected:
151 self.connected = True
152 pass
153 else:
154 self.send_be_create()
156 def send_be_create(self):
157 log.debug("send_be_create %s", str(self))
158 msg = packMsg('blkif_be_create_t',
159 { 'domid' : self.frontendDomain,
160 'blkif_handle' : self.id })
161 msg = self.backendChannel.requestResponse(msg)
162 #todo: check return status
163 self.connected = True
165 def destroy(self, change=False, reboot=False):
166 """Disconnect from the blkif control interface and destroy it.
167 """
168 self.send_be_disconnect()
169 self.send_be_destroy()
170 self.closeEvtchn()
171 self.destroyed = True
172 # For change true need to notify front-end, or back-end will do it?
174 def send_be_disconnect(self):
175 msg = packMsg('blkif_be_disconnect_t',
176 { 'domid' : self.frontendDomain,
177 'blkif_handle' : self.id })
178 self.backendChannel.writeRequest(msg)
179 self.connected = False
181 def send_be_destroy(self):
182 msg = packMsg('blkif_be_destroy_t',
183 { 'domid' : self.frontendDomain,
184 'blkif_handle' : self.id })
185 self.backendChannel.writeRequest(msg)
187 def connectInterface(self, val):
188 self.openEvtchn()
189 log.debug("Connecting blkif to event channel %s ports=%d:%d",
190 str(self), self.evtchn['port1'], self.evtchn['port2'])
191 msg = packMsg('blkif_be_connect_t',
192 { 'domid' : self.frontendDomain,
193 'blkif_handle' : self.id,
194 'evtchn' : self.getEventChannelBackend(),
195 'shmem_frame' : val['shmem_frame'] })
196 msg = self.backendChannel.requestResponse(msg)
197 #todo: check return status
198 val = unpackMsg('blkif_be_connect_t', msg)
199 self.status = BLKIF_INTERFACE_STATUS_CONNECTED
200 self.send_fe_interface_status()
202 def send_fe_interface_status(self):
203 msg = packMsg('blkif_fe_interface_status_t',
204 { 'handle' : self.id,
205 'status' : self.status,
206 'domid' : self.backendDomain,
207 'evtchn' : self.getEventChannelFrontend() })
208 self.frontendChannel.writeRequest(msg)
210 def interfaceDisconnected(self):
211 self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
212 #todo?: Close evtchn:
213 #self.closeEvtchn()
214 self.send_fe_interface_status()
216 def interfaceChanged(self):
217 """Notify the front-end that devices have been added or removed.
218 The front-end should then probe for devices.
219 """
220 msg = packMsg('blkif_fe_interface_status_t',
221 { 'handle' : self.id,
222 'status' : BLKIF_INTERFACE_STATUS_CHANGED,
223 'domid' : self.backendDomain,
224 'evtchn' : 0 })
225 self.frontendChannel.writeRequest(msg)
227 class BlkDev(Dev):
228 """Info record for a block device.
229 """
231 def __init__(self, controller, id, config, recreate=False):
232 Dev.__init__(self, controller, id, config, recreate=recreate)
233 self.dev = None
234 self.uname = None
235 self.vdev = None
236 self.mode = None
237 self.type = None
238 self.params = None
239 self.node = None
240 self.device = None
241 self.start_sector = None
242 self.nr_sectors = None
244 self.frontendDomain = self.getDomain()
245 self.frontendChannel = None
246 self.backendDomain = None
247 self.backendChannel = None
248 self.backendId = 0
249 self.configure(self.config, recreate=recreate)
251 def init(self, recreate=False, reboot=False):
252 self.frontendDomain = self.getDomain()
253 self.frontendChannel = self.getChannel()
254 backend = self.getBackend()
255 self.backendChannel = backend.backendChannel
256 self.backendId = backend.id
258 def configure(self, config, change=False, recreate=False):
259 if change:
260 raise XendError("cannot reconfigure vbd")
261 self.config = config
262 self.uname = sxp.child_value(config, 'uname')
263 if not self.uname:
264 raise VmError('vbd: Missing uname')
265 # Split into type and type-specific params (which are passed to the
266 # type-specific control script).
267 (self.type, self.params) = string.split(self.uname, ':', 1)
268 self.dev = sxp.child_value(config, 'dev')
269 if not self.dev:
270 raise VmError('vbd: Missing dev')
271 self.mode = sxp.child_value(config, 'mode', 'r')
273 self.vdev = blkdev_name_to_number(self.dev)
274 if not self.vdev:
275 raise VmError('vbd: Device not found: %s' % self.dev)
277 try:
278 self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
279 except:
280 raise XendError('invalid backend domain')
282 return self.config
284 def attach(self, recreate=False, change=False):
285 if recreate:
286 node = sxp.child_value(recreate, 'node')
287 print 'BlkDev>attach>', 'recreate=', recreate, 'node=', node
288 self.setNode(node)
289 else:
290 node = Blkctl.block('bind', self.type, self.params)
291 self.setNode(node)
292 self.attachBackend()
293 if change:
294 self.interfaceChanged()
296 def unbind(self):
297 if self.node is None: return
298 log.debug("Unbinding vbd (type %s) from %s"
299 % (self.type, self.node))
300 Blkctl.block('unbind', self.type, self.node)
302 def setNode(self, node):
304 # NOTE:
305 # This clause is testing code for storage system experiments.
306 # Add a new disk type that will just pass an opaque id in the
307 # start_sector and use an experimental device type.
308 # Please contact andrew.warfield@cl.cam.ac.uk with any concerns.
309 if self.type == 'parallax':
310 self.node = node
311 self.device = 61440 # (240,0)
312 self.start_sector = long(self.params)
313 self.nr_sectors = long(0)
314 return
315 # done.
317 mounted_mode = self.check_mounted(node)
318 if not '!' in self.mode and mounted_mode:
319 if mounted_mode == "w":
320 raise VmError("vbd: Segment %s is in writable use" %
321 self.uname)
322 elif 'w' in self.mode:
323 raise VmError("vbd: Segment %s is in read-only use" %
324 self.uname)
326 segment = blkdev_segment(node)
327 if not segment:
328 raise VmError("vbd: Segment not found: uname=%s" % self.uname)
329 self.node = node
330 self.device = segment['device']
331 self.start_sector = segment['start_sector']
332 self.nr_sectors = segment['nr_sectors']
334 def check_mounted(self, name):
335 mode = mount_mode(name)
336 xd = XendRoot.get_component('xen.xend.XendDomain')
337 for vm in xd.domains():
338 ctrl = vm.getDeviceController(self.getType(), error=False)
339 if (not ctrl): continue
340 for dev in ctrl.getDevices():
341 if dev is self: continue
342 if dev.type == 'phy' and name == expand_dev_name(dev.params):
343 mode = dev.mode
344 if 'w' in mode:
345 return 'w'
346 if mode and 'r' in mode:
347 return 'r'
348 return None
350 def readonly(self):
351 return 'w' not in self.mode
353 def sxpr(self):
354 val = ['vbd',
355 ['id', self.id],
356 ['vdev', self.vdev],
357 ['device', self.device],
358 ['mode', self.mode]]
359 if self.dev:
360 val.append(['dev', self.dev])
361 if self.uname:
362 val.append(['uname', self.uname])
363 if self.node:
364 val.append(['node', self.node])
365 val.append(['index', self.getIndex()])
366 return val
368 def getBackend(self):
369 return self.controller.getBackend(self.backendDomain)
371 def refresh(self):
372 log.debug("Refreshing vbd domain=%d id=%s", self.frontendDomain, self.id)
373 self.interfaceChanged()
375 def destroy(self, change=False, reboot=False):
376 """Destroy the device. If 'change' is true notify the front-end interface.
378 @param change: change flag
379 """
380 self.destroyed = True
381 log.debug("Destroying vbd domain=%d id=%s", self.frontendDomain, self.id)
382 self.send_be_vbd_destroy()
383 if change:
384 self.interfaceChanged()
385 self.unbind()
387 def interfaceChanged(self):
388 """Tell the back-end to notify the front-end that a device has been
389 added or removed.
390 """
391 self.getBackend().interfaceChanged()
393 def attachBackend(self):
394 """Attach the device to its controller.
396 """
397 self.getBackend().connect()
398 self.send_be_vbd_create()
400 def send_be_vbd_create(self):
401 msg = packMsg('blkif_be_vbd_create_t',
402 { 'domid' : self.frontendDomain,
403 'blkif_handle' : self.backendId,
404 'pdevice' : self.device,
405 'vdevice' : self.vdev,
406 'readonly' : self.readonly() })
407 msg = self.backendChannel.requestResponse(msg)
409 val = unpackMsg('blkif_be_vbd_create_t', msg)
410 status = val['status']
411 if status != BLKIF_BE_STATUS_OKAY:
412 raise XendError("Creating vbd failed: device %s, error %d"
413 % (sxp.to_string(self.config), status))
415 def send_be_vbd_destroy(self):
416 msg = packMsg('blkif_be_vbd_destroy_t',
417 { 'domid' : self.frontendDomain,
418 'blkif_handle' : self.backendId,
419 'vdevice' : self.vdev })
420 return self.backendChannel.writeRequest(msg)
422 class BlkifController(DevController):
423 """Block device interface controller. Handles all block devices
424 for a domain.
425 """
427 def __init__(self, dctype, vm, recreate=False):
428 """Create a block device controller.
429 """
430 DevController.__init__(self, dctype, vm, recreate=recreate)
431 self.backends = {}
432 self.backendId = 0
433 self.rcvr = None
435 def initController(self, recreate=False, reboot=False):
436 self.destroyed = False
437 # Add our handlers for incoming requests.
438 self.rcvr = CtrlMsgRcvr(self.getChannel())
439 self.rcvr.addHandler(CMSG_BLKIF_FE,
440 CMSG_BLKIF_FE_DRIVER_STATUS,
441 self.recv_fe_driver_status)
442 self.rcvr.addHandler(CMSG_BLKIF_FE,
443 CMSG_BLKIF_FE_INTERFACE_CONNECT,
444 self.recv_fe_interface_connect)
445 self.rcvr.registerChannel()
446 if reboot:
447 self.rebootBackends()
448 self.rebootDevices()
450 def sxpr(self):
451 val = ['blkif', ['dom', self.getDomain()]]
452 return val
454 def rebootBackends(self):
455 for backend in self.backends.values():
456 backend.init(reboot=True)
458 def getBackendById(self, id):
459 return self.backends.get(id)
461 def getBackendByDomain(self, dom):
462 for backend in self.backends.values():
463 if backend.backendDomain == dom:
464 return backend
465 return None
467 def getBackend(self, dom):
468 backend = self.getBackendByDomain(dom)
469 if backend: return backend
470 backend = BlkifBackend(self, self.backendId, dom)
471 self.backendId += 1
472 self.backends[backend.getId()] = backend
473 backend.init()
474 return backend
476 def newDevice(self, id, config, recreate=False):
477 """Create a device..
479 @param id: device id
480 @param config: device configuration
481 @param recreate: if true it's being recreated (after xend restart)
482 @type recreate: bool
483 @return: device
484 @rtype: BlkDev
485 """
486 return BlkDev(self, id, config, recreate=recreate)
488 def destroyController(self, reboot=False):
489 """Destroy the controller and all devices.
490 """
491 self.destroyed = True
492 log.debug("Destroying blkif domain=%d", self.getDomain())
493 self.destroyDevices(reboot=reboot)
494 self.destroyBackends(reboot=reboot)
495 self.rcvr.deregisterChannel()
497 def destroyBackends(self, reboot=False):
498 for backend in self.backends.values():
499 backend.destroy(reboot=reboot)
501 def recv_fe_driver_status(self, msg):
502 val = unpackMsg('blkif_fe_driver_status_t', msg)
503 for backend in self.backends.values():
504 backend.interfaceDisconnected()
506 def recv_fe_interface_connect(self, msg):
507 val = unpackMsg('blkif_fe_interface_connect_t', msg)
508 id = val['handle']
509 backend = self.getBackendById(id)
510 if backend:
511 try:
512 backend.connectInterface(val)
513 except IOError, ex:
514 log.error("Exception connecting backend: %s", ex)
515 else:
516 log.error('interface connect on unknown interface: id=%d', id)