ia64/xen-unstable

view tools/python/xen/xend/server/DevController.py @ 7595:286946489e5d

From Murillo Fernandes Bernardes <mfb@br.ibm.com>:

The problem is: There is no mechanism to detect block device setup failure

Network devices have the same problem, and are fixed with this too.

I handling this problem in the way suggested by aliguori:
- hotplug scripts write a "hotplug-status" node on store
- Xend DevController.createDevice() check verify this node and return success
or throw an exception on failure.
- If no changes in "hotplug-status" node after DEVICE_CREATE_TIMEOUT seconds
Xend throw an exception showing the problem with hotplug scripts.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Mon Oct 31 17:10:57 2005 +0100 (2005-10-31)
parents c774efa2aca8
children 365245f2a106
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 # Copyright (C) 2005 XenSource Ltd
17 #============================================================================
19 from threading import Event
21 from xen.xend import sxp
22 from xen.xend.XendError import VmError
23 from xen.xend.XendLogging import log
25 from xen.xend.xenstore.xstransact import xstransact
26 from xen.xend.xenstore.xswatch import xswatch
28 DEVICE_CREATE_TIMEOUT = 120
29 HOTPLUG_STATUS_NODE = "hotplug-status"
30 HOTPLUG_STATUS_ERROR = "error"
32 class DevController:
33 """Abstract base class for a device controller. Device controllers create
34 appropriate entries in the store to trigger the creation, reconfiguration,
35 and destruction of devices in guest domains. Each subclass of
36 DevController is responsible for a particular device-class, and
37 understands the details of configuration specific to that device-class.
39 DevController itself provides the functionality common to all device
40 creation tasks, as well as providing an interface to XendDomainInfo for
41 triggering those events themselves.
42 """
44 # Set when registered.
45 deviceClass = None
48 ## public:
50 def __init__(self, vm):
51 self.vm = vm
54 def createDevice(self, config):
55 """Trigger the creation of a device with the given configuration.
57 @return The ID for the newly created device.
58 """
59 (devid, back, front) = self.getDeviceDetails(config)
61 self.writeDetails(config, devid, back, front)
63 status, fn_ret = self.waitForBackend(devid)
64 if status:
65 self.destroyDevice(devid)
66 raise VmError( ("Device %s (%s) could not be connected. "
67 "Hotplug scripts not working")
68 % (devid, self.deviceClass))
70 elif fn_ret == HOTPLUG_STATUS_ERROR:
71 self.destroyDevice(devid)
72 raise VmError( ("Device %s (%s) could not be connected. "
73 "Backend device not found!")
74 % (devid, self.deviceClass))
75 return devid
78 def reconfigureDevice(self, devid, config):
79 """Reconfigure the specified device.
81 The implementation here just raises VmError. This may be overridden
82 by those subclasses that can reconfigure their devices.
83 """
84 raise VmError('%s devices may not be reconfigured' % self.deviceClass)
87 def destroyDevice(self, devid):
88 """Destroy the specified device.
90 @param devid The device ID, or something device-specific from which
91 the device ID can be determined (such as a guest-side device name).
93 The implementation here simply deletes the appropriate paths from the
94 store. This may be overridden by subclasses who need to perform other
95 tasks on destruction. Further, the implementation here can only
96 accept integer device IDs, or values that can be converted to
97 integers. Subclasses may accept other values and convert them to
98 integers before passing them here.
99 """
101 devid = int(devid)
103 frontpath = self.frontendPath(devid)
104 backpath = xstransact.Read(frontpath, "backend")
106 xstransact.Remove(frontpath)
108 if backpath:
109 xstransact.Remove(backpath)
110 else:
111 raise VmError("Device %s not connected" % devid)
114 def configurations(self):
115 return map(self.configuration, self.deviceIDs())
118 def configuration(self, devid):
119 """@return an s-expression giving the current configuration of the
120 specified device. This would be suitable for giving to {@link
121 #createDevice} in order to recreate that device."""
123 backdomid = int(xstransact.Read(self.frontendPath(devid),
124 "backend-id"))
126 return [self.deviceClass, ['backend', backdomid]]
129 def sxprs(self):
130 """@return an s-expression describing all the devices of this
131 controller's device-class.
132 """
133 return xstransact.ListRecursive(self.frontendRoot())
136 def sxpr(self, devid):
137 """@return an s-expression describing the specified device.
138 """
139 return [self.deviceClass, ['dom', self.vm.getDomid(),
140 'id', devid]]
143 ## protected:
145 def getDeviceDetails(self, config):
146 """Compute the details for creation of a device corresponding to the
147 given configuration. These details consist of a tuple of (devID,
148 backDetails, frontDetails), where devID is the ID for the new device,
149 and backDetails and frontDetails are the device configuration
150 specifics for the backend and frontend respectively.
152 backDetails and frontDetails should be dictionaries, the keys and
153 values of which will be used as paths in the store. There is no need
154 for these dictionaries to include the references from frontend to
155 backend, nor vice versa, as these will be handled by DevController.
157 Abstract; must be implemented by every subclass.
159 @return (devID, backDetails, frontDetails), as specified above.
160 """
162 raise NotImplementedError()
165 def getDomid(self):
166 """Stub to {@link XendDomainInfo.getDomid}, for use by our
167 subclasses.
168 """
169 return self.vm.getDomid()
172 def allocateDeviceID(self):
173 """Allocate a device ID, allocating them consecutively on a
174 per-domain, per-device-class basis, and using the store to record the
175 next available ID.
177 This method is available to our subclasses, though it is not
178 compulsory to use it; subclasses may prefer to allocate IDs based upon
179 the device configuration instead.
180 """
181 path = self.frontendMiscPath()
182 while True:
183 t = xstransact(path)
184 try:
185 result = t.read("nextDeviceID")
186 if result:
187 result = int(result)
188 else:
189 result = 0
190 t.write("nextDeviceID", str(result + 1))
191 if t.commit():
192 return result
193 except:
194 t.abort()
195 raise
198 def readBackend(self, devid, *args):
199 frontpath = self.frontendPath(devid)
200 backpath = xstransact.Read(frontpath, "backend")
201 return xstransact.Read(backpath, *args)
204 def deviceIDs(self):
205 """@return The IDs of each of the devices currently configured for
206 this instance's deviceClass.
207 """
208 return map(int, xstransact.List(self.frontendRoot()))
211 ## private:
213 def writeDetails(self, config, devid, backDetails, frontDetails):
214 """Write the details in the store to trigger creation of a device.
215 The backend domain ID is taken from the given config, paths for
216 frontend and backend are computed, and these are written to the store
217 appropriately, including references from frontend to backend and vice
218 versa.
220 @param config The configuration of the device, as given to
221 {@link #createDevice}.
222 @param devid As returned by {@link #getDeviceDetails}.
223 @param backDetails As returned by {@link #getDeviceDetails}.
224 @param frontDetails As returned by {@link #getDeviceDetails}.
225 """
227 import xen.xend.XendDomain
228 xd = xen.xend.XendDomain.instance()
230 backdom_name = sxp.child_value(config, 'backend')
231 if backdom_name:
232 backdom = xd.domain_lookup_by_name_or_id_nr(backdom_name)
233 else:
234 backdom = xd.privilegedDomain()
236 if not backdom:
237 raise VmError("Cannot configure device for unknown backend %s" %
238 backdom_name)
240 frontpath = self.frontendPath(devid)
241 backpath = self.backendPath(backdom, devid)
243 frontDetails.update({
244 'backend' : backpath,
245 'backend-id' : "%i" % backdom.getDomid()
246 })
249 backDetails.update({
250 'domain' : self.vm.getName(),
251 'frontend' : frontpath,
252 'frontend-id' : "%i" % self.vm.getDomid()
253 })
255 log.debug('DevController: writing %s to %s.', str(frontDetails),
256 frontpath)
257 log.debug('DevController: writing %s to %s.', str(backDetails),
258 backpath)
260 xstransact.Write(frontpath, frontDetails)
261 xstransact.Write(backpath, backDetails)
263 def waitForBackend(self,devid):
264 ev = Event()
266 def hotplugStatus():
267 status = self.readBackend(devid, HOTPLUG_STATUS_NODE)
268 if status is not None:
269 watch.xs.unwatch(backpath, watch)
270 hotplugStatus.value = status
271 ev.set()
273 hotplugStatus.value = None
274 frontpath = self.frontendPath(devid)
275 backpath = xstransact.Read(frontpath, "backend")
277 watch = xswatch(backpath, hotplugStatus)
279 ev.wait(DEVICE_CREATE_TIMEOUT)
280 if ev.isSet():
281 return (0, hotplugStatus.value)
282 else:
283 return (-1, hotplugStatus.value)
287 def backendPath(self, backdom, devid):
288 """@param backdom [XendDomainInfo] The backend domain info."""
290 return "%s/backend/%s/%s/%d" % (backdom.getDomainPath(),
291 self.deviceClass,
292 self.vm.getDomid(), devid)
295 def frontendPath(self, devid):
296 return "%s/%d" % (self.frontendRoot(), devid)
299 def frontendRoot(self):
300 return "%s/device/%s" % (self.vm.getDomainPath(), self.deviceClass)
303 def frontendMiscPath(self):
304 return "%s/device-misc/%s" % (self.vm.getDomainPath(),
305 self.deviceClass)