xen-vtx-unstable

view tools/python/xen/xend/server/blkif.py @ 6774:4d899a738d59

merge?
author cl349@firebug.cl.cam.ac.uk
date Tue Sep 13 15:05:49 2005 +0000 (2005-09-13)
parents 7d0fb56b4a91
children
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 #============================================================================
18 """Support for virtual block devices.
19 """
20 import string
22 from xen.util import blkif
23 from xen.xend.XendError import XendError, VmError
24 from xen.xend.XendRoot import get_component
25 from xen.xend.XendLogging import log
26 from xen.xend import sxp
27 from xen.xend import Blkctl
28 from xen.xend.xenstore import DBVar
30 from xen.xend.server.controller import Dev, DevController
32 class BlkifBackend:
33 """ Handler for the 'back-end' channel to a block device driver domain
34 on behalf of a front-end domain.
35 Must be connected using connect() before it can be used.
36 """
38 def __init__(self, controller, id, dom, recreate=False):
39 self.controller = controller
40 self.id = id
41 self.frontendDomain = self.controller.getDomain()
42 self.backendDomain = dom
43 self.destroyed = False
44 self.connected = False
45 self.status = None
47 def init(self, recreate=False, reboot=False):
48 self.destroyed = False
49 self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
50 self.frontendDomain = self.controller.getDomain()
52 def __str__(self):
53 return ('<BlkifBackend frontend=%d backend=%d id=%d>'
54 % (self.frontendDomain,
55 self.backendDomain,
56 self.id))
58 def getId(self):
59 return self.id
61 def connect(self, recreate=False):
62 """Connect to the blkif control interface.
64 @param recreate: true if after xend restart
65 """
66 log.debug("Connecting blkif %s", str(self))
67 if recreate or self.connected:
68 self.connected = True
69 pass
71 def destroy(self, change=False, reboot=False):
72 """Disconnect from the blkif control interface and destroy it.
73 """
74 self.destroyed = True
75 # For change true need to notify front-end, or back-end will do it?
77 def connectInterface(self, val):
78 self.status = BLKIF_INTERFACE_STATUS_CONNECTED
80 def interfaceDisconnected(self):
81 self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
83 class BlkDev(Dev):
84 """Info record for a block device.
85 """
87 __exports__ = Dev.__exports__ + [
88 DBVar('dev', ty='str'),
89 DBVar('vdev', ty='int'),
90 DBVar('mode', ty='str'),
91 DBVar('viftype', ty='str'),
92 DBVar('params', ty='str'),
93 DBVar('node', ty='str'),
94 DBVar('device', ty='long'),
95 DBVar('dev_handle', ty='long'),
96 DBVar('start_sector', ty='long'),
97 DBVar('nr_sectors', ty='long'),
98 ]
100 def __init__(self, controller, id, config, recreate=False):
101 Dev.__init__(self, controller, id, config, recreate=recreate)
102 self.dev = None
103 self.uname = None
104 self.vdev = None
105 self.mode = None
106 self.type = None
107 self.params = None
108 self.node = None
109 self.device = None
110 self.dev_handle = 0
111 self.start_sector = None
112 self.nr_sectors = None
114 self.frontendDomain = self.getDomain()
115 self.backendDomain = None
116 self.backendId = 0
117 self.configure(self.config, recreate=recreate)
119 def exportToDB(self, save=False):
120 Dev.exportToDB(self, save=save)
121 backend = self.getBackend()
123 def init(self, recreate=False, reboot=False):
124 self.frontendDomain = self.getDomain()
125 backend = self.getBackend()
126 self.backendId = backend.id
128 def configure(self, config, change=False, recreate=False):
129 if change:
130 raise XendError("cannot reconfigure vbd")
131 self.config = config
132 self.uname = sxp.child_value(config, 'uname')
133 if not self.uname:
134 raise VmError('vbd: Missing uname')
135 # Split into type and type-specific params (which are passed to the
136 # type-specific control script).
137 (self.type, self.params) = string.split(self.uname, ':', 1)
138 self.dev = sxp.child_value(config, 'dev')
139 if not self.dev:
140 raise VmError('vbd: Missing dev')
141 self.mode = sxp.child_value(config, 'mode', 'r')
143 self.vdev = blkif.blkdev_name_to_number(self.dev)
144 if not self.vdev:
145 raise VmError('vbd: Device not found: %s' % self.dev)
147 try:
148 xd = get_component('xen.xend.XendDomain')
149 self.backendDomain = xd.domain_lookup_by_name(sxp.child_value(config, 'backend', '0')).id
150 except:
151 raise XendError('invalid backend domain')
153 return self.config
155 def attach(self, recreate=False, change=False):
156 if recreate:
157 pass
158 else:
159 node = Blkctl.block('bind', self.type, self.params)
160 self.setNode(node)
161 self.attachBackend()
162 if change:
163 self.interfaceChanged()
165 def unbind(self):
166 if self.node is None: return
167 log.debug("Unbinding vbd (type %s) from %s"
168 % (self.type, self.node))
169 Blkctl.block('unbind', self.type, self.node)
171 def setNode(self, node):
173 # NOTE:
174 # This clause is testing code for storage system experiments.
175 # Add a new disk type that will just pass an opaque id in the
176 # dev_handle and use an experimental device type.
177 # Please contact andrew.warfield@cl.cam.ac.uk with any concerns.
178 if self.type == 'parallax':
179 self.node = node
180 self.device = 61440 # (240,0)
181 self.dev_handle = long(self.params)
182 self.nr_sectors = long(0)
183 return
184 # done.
186 mounted_mode = self.check_mounted(node)
187 if not '!' in self.mode and mounted_mode:
188 if mounted_mode == "w":
189 raise VmError("vbd: Segment %s is in writable use" %
190 self.uname)
191 elif 'w' in self.mode:
192 raise VmError("vbd: Segment %s is in read-only use" %
193 self.uname)
195 segment = blkif.blkdev_segment(node)
196 if not segment:
197 raise VmError("vbd: Segment not found: uname=%s" % self.uname)
198 self.node = node
199 self.device = segment['device']
200 self.start_sector = segment['start_sector']
201 self.nr_sectors = segment['nr_sectors']
203 def check_mounted(self, name):
204 mode = blkif.mount_mode(name)
205 xd = get_component('xen.xend.XendDomain')
206 for vm in xd.list():
207 ctrl = vm.getDeviceController(self.getType(), error=False)
208 if (not ctrl): continue
209 for dev in ctrl.getDevices():
210 if dev is self: continue
211 if dev.type == 'phy' and name == blkif.expand_dev_name(dev.params):
212 mode = dev.mode
213 if 'w' in mode:
214 return 'w'
215 if mode and 'r' in mode:
216 return 'r'
217 return None
219 def readonly(self):
220 return 'w' not in self.mode
222 def sxpr(self):
223 val = ['vbd',
224 ['id', self.id],
225 ['vdev', self.vdev],
226 ['device', self.device],
227 ['mode', self.mode]]
228 if self.dev:
229 val.append(['dev', self.dev])
230 if self.uname:
231 val.append(['uname', self.uname])
232 if self.node:
233 val.append(['node', self.node])
234 return val
236 def getBackend(self):
237 return self.controller.getBackend(self.backendDomain)
239 def refresh(self):
240 log.debug("Refreshing vbd domain=%d id=%s", self.frontendDomain,
241 self.id)
242 self.interfaceChanged()
244 def destroy(self, change=False, reboot=False):
245 """Destroy the device. If 'change' is true notify the front-end interface.
247 @param change: change flag
248 """
249 self.destroyed = True
250 log.debug("Destroying vbd domain=%d id=%s", self.frontendDomain,
251 self.id)
252 if change:
253 self.interfaceChanged()
254 self.unbind()
256 def interfaceChanged(self):
257 """Tell the back-end to notify the front-end that a device has been
258 added or removed.
259 """
260 self.getBackend().interfaceChanged()
262 def attachBackend(self):
263 """Attach the device to its controller.
265 """
266 self.getBackend().connect()
268 class BlkifController(DevController):
269 """Block device interface controller. Handles all block devices
270 for a domain.
271 """
273 def __init__(self, vm, recreate=False):
274 """Create a block device controller.
275 """
276 DevController.__init__(self, vm, recreate=recreate)
277 self.backends = {}
278 self.backendId = 0
280 def initController(self, recreate=False, reboot=False):
281 self.destroyed = False
282 if reboot:
283 self.rebootBackends()
284 self.rebootDevices()
286 def sxpr(self):
287 val = ['blkif', ['dom', self.getDomain()]]
288 return val
290 def rebootBackends(self):
291 for backend in self.backends.values():
292 backend.init(reboot=True)
294 def getBackendById(self, id):
295 return self.backends.get(id)
297 def getBackendByDomain(self, dom):
298 for backend in self.backends.values():
299 if backend.backendDomain == dom:
300 return backend
301 return None
303 def getBackend(self, dom):
304 backend = self.getBackendByDomain(dom)
305 if backend: return backend
306 backend = BlkifBackend(self, self.backendId, dom)
307 self.backendId += 1
308 self.backends[backend.getId()] = backend
309 backend.init()
310 return backend
312 def newDevice(self, id, config, recreate=False):
313 """Create a device..
315 @param id: device id
316 @param config: device configuration
317 @param recreate: if true it's being recreated (after xend restart)
318 @type recreate: bool
319 @return: device
320 @rtype: BlkDev
321 """
322 return BlkDev(self, id, config, recreate=recreate)
324 def destroyController(self, reboot=False):
325 """Destroy the controller and all devices.
326 """
327 self.destroyed = True
328 log.debug("Destroying blkif domain=%d", self.getDomain())
329 self.destroyDevices(reboot=reboot)
330 self.destroyBackends(reboot=reboot)
332 def destroyBackends(self, reboot=False):
333 for backend in self.backends.values():
334 backend.destroy(reboot=reboot)