debuggers.hg

view tools/python/xen/xend/image.py @ 10959:4c2fab8f8c34

[qemu] Use xenstore to configure ioemu block devices.
- read ioemu block device config from xenstore
- don't require the ioemu: prefix on block devices any longer
- allow change of media associated with cdrom drives
- replace cdrom= option by :cdrom suffix on regular block device config:
'file:/root/mytest.iso,hdc:cdrom,r'
- don't create default cdrom drive anymore - to create default empty
cdrom drive use: ',hdc:cdrom,r'

Signed-off-by: Christian Limpach <Christian.Limpach@xensource.com>
author chris@kneesaa.uk.xensource.com
date Thu Aug 03 18:28:29 2006 +0100 (2006-08-03)
parents ecb8ff1fcf1f
children 92ef1215a6f1
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) 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (C) 2005 XenSource Ltd
17 #============================================================================
20 import os, string
21 import re
22 import math
24 import xen.lowlevel.xc
25 from xen.xend import sxp
26 from xen.xend.XendError import VmError
27 from xen.xend.XendLogging import log
28 from xen.xend.server.netif import randomMAC
29 from xen.xend.xenstore.xswatch import xswatch
32 xc = xen.lowlevel.xc.xc()
35 MAX_GUEST_CMDLINE = 1024
38 def create(vm, imageConfig, deviceConfig):
39 """Create an image handler for a vm.
41 @return ImageHandler instance
42 """
43 return findImageHandlerClass(imageConfig)(vm, imageConfig, deviceConfig)
46 class ImageHandler:
47 """Abstract base class for image handlers.
49 createImage() is called to configure and build the domain from its
50 kernel image and ramdisk etc.
52 The method buildDomain() is used to build the domain, and must be
53 defined in a subclass. Usually this is the only method that needs
54 defining in a subclass.
56 The method createDeviceModel() is called to create the domain device
57 model if it needs one. The default is to do nothing.
59 The method destroy() is called when the domain is destroyed.
60 The default is to do nothing.
61 """
63 ostype = None
66 def __init__(self, vm, imageConfig, deviceConfig):
67 self.vm = vm
69 self.kernel = None
70 self.ramdisk = None
71 self.cmdline = None
73 self.configure(imageConfig, deviceConfig)
75 def configure(self, imageConfig, _):
76 """Config actions common to all unix-like domains."""
78 def get_cfg(name, default = None):
79 return sxp.child_value(imageConfig, name, default)
81 self.kernel = get_cfg("kernel")
82 self.cmdline = ""
83 ip = get_cfg("ip")
84 if ip:
85 self.cmdline += " ip=" + ip
86 root = get_cfg("root")
87 if root:
88 self.cmdline += " root=" + root
89 args = get_cfg("args")
90 if args:
91 self.cmdline += " " + args
92 self.ramdisk = get_cfg("ramdisk", '')
94 self.vm.storeVm(("image/ostype", self.ostype),
95 ("image/kernel", self.kernel),
96 ("image/cmdline", self.cmdline),
97 ("image/ramdisk", self.ramdisk))
100 def cleanupBootloading(self):
101 self.unlink(self.kernel)
102 self.unlink(self.ramdisk)
105 def unlink(self, f):
106 if not f: return
107 try:
108 os.unlink(f)
109 except OSError, ex:
110 log.warning("error removing bootloader file '%s': %s", f, ex)
113 def createImage(self):
114 """Entry point to create domain memory image.
115 Override in subclass if needed.
116 """
117 return self.createDomain()
120 def createDomain(self):
121 """Build the domain boot image.
122 """
123 # Set params and call buildDomain().
125 if not os.path.isfile(self.kernel):
126 raise VmError('Kernel image does not exist: %s' % self.kernel)
127 if self.ramdisk and not os.path.isfile(self.ramdisk):
128 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
129 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
130 log.warning('kernel cmdline too long, domain %d',
131 self.vm.getDomid())
133 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
134 self.vm.getDomid(), self.vm.getVCpuCount())
136 result = self.buildDomain()
138 if isinstance(result, dict):
139 return result
140 else:
141 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
142 % (self.ostype, self.vm.getDomid(), str(result)))
145 def getDomainMemory(self, mem_kb):
146 """@return The memory required, in KiB, by the domain to store the
147 given amount, also in KiB."""
148 if os.uname()[4] != 'ia64':
149 # A little extra because auto-ballooning is broken w.r.t. HVM
150 # guests. Also, slack is necessary for live migration since that
151 # uses shadow page tables.
152 if 'hvm' in xc.xeninfo()['xen_caps']:
153 mem_kb += 4*1024;
154 return mem_kb
156 def buildDomain(self):
157 """Build the domain. Define in subclass."""
158 raise NotImplementedError()
160 def createDeviceModel(self):
161 """Create device model for the domain (define in subclass if needed)."""
162 pass
164 def destroy(self):
165 """Extra cleanup on domain destroy (define in subclass if needed)."""
166 pass
169 class LinuxImageHandler(ImageHandler):
171 ostype = "linux"
173 def buildDomain(self):
174 store_evtchn = self.vm.getStorePort()
175 console_evtchn = self.vm.getConsolePort()
177 log.debug("dom = %d", self.vm.getDomid())
178 log.debug("image = %s", self.kernel)
179 log.debug("store_evtchn = %d", store_evtchn)
180 log.debug("console_evtchn = %d", console_evtchn)
181 log.debug("cmdline = %s", self.cmdline)
182 log.debug("ramdisk = %s", self.ramdisk)
183 log.debug("vcpus = %d", self.vm.getVCpuCount())
184 log.debug("features = %s", self.vm.getFeatures())
186 return xc.linux_build(dom = self.vm.getDomid(),
187 image = self.kernel,
188 store_evtchn = store_evtchn,
189 console_evtchn = console_evtchn,
190 cmdline = self.cmdline,
191 ramdisk = self.ramdisk,
192 features = self.vm.getFeatures())
194 class HVMImageHandler(ImageHandler):
196 ostype = "hvm"
198 def configure(self, imageConfig, deviceConfig):
199 ImageHandler.configure(self, imageConfig, deviceConfig)
201 info = xc.xeninfo()
202 if not 'hvm' in info['xen_caps']:
203 raise VmError("Not an HVM capable platform, we stop creating!")
205 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
206 self.device_model = sxp.child_value(imageConfig, 'device_model')
207 if not self.device_model:
208 raise VmError("hvm: missing device model")
209 self.display = sxp.child_value(imageConfig, 'display')
210 self.xauthority = sxp.child_value(imageConfig, 'xauthority')
211 self.vncconsole = sxp.child_value(imageConfig, 'vncconsole')
213 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
214 ("image/device-model", self.device_model),
215 ("image/display", self.display))
217 self.pid = 0
219 self.dmargs += self.configVNC(imageConfig)
221 self.pae = int(sxp.child_value(imageConfig, 'pae', 0))
223 self.acpi = int(sxp.child_value(imageConfig, 'acpi', 0))
224 self.apic = int(sxp.child_value(imageConfig, 'apic', 0))
226 def buildDomain(self):
227 store_evtchn = self.vm.getStorePort()
229 log.debug("dom = %d", self.vm.getDomid())
230 log.debug("image = %s", self.kernel)
231 log.debug("store_evtchn = %d", store_evtchn)
232 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
233 log.debug("vcpus = %d", self.vm.getVCpuCount())
234 log.debug("pae = %d", self.pae)
235 log.debug("acpi = %d", self.acpi)
236 log.debug("apic = %d", self.apic)
238 self.register_shutdown_watch()
240 return xc.hvm_build(dom = self.vm.getDomid(),
241 image = self.kernel,
242 store_evtchn = store_evtchn,
243 memsize = self.vm.getMemoryTarget() / 1024,
244 vcpus = self.vm.getVCpuCount(),
245 pae = self.pae,
246 acpi = self.acpi,
247 apic = self.apic)
249 # Return a list of cmd line args to the device models based on the
250 # xm config file
251 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
252 dmargs = [ 'boot', 'fda', 'fdb', 'audio',
253 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
254 'usb', 'usbdevice']
255 ret = []
256 for a in dmargs:
257 v = sxp.child_value(imageConfig, a)
259 # python doesn't allow '-' in variable names
260 if a == 'stdvga': a = 'std-vga'
261 if a == 'audio': a = 'enable-audio'
263 # Handle booleans gracefully
264 if a in ['localtime', 'std-vga', 'isa', 'enable-audio', 'usb']:
265 if v != None: v = int(v)
266 if v: ret.append("-%s" % a)
267 else:
268 if v:
269 ret.append("-%s" % a)
270 ret.append("%s" % v)
271 log.debug("args: %s, val: %s" % (a,v))
273 # Handle disk/network related options
274 mac = None
275 ret = ret + ["-domain-name", "%s" % self.vm.info['name']]
276 nics = 0
277 for (name, info) in deviceConfig:
278 if name == 'vbd':
279 uname = sxp.child_value(info, 'uname')
280 if 'file:' in uname:
281 (_, vbdparam) = string.split(uname, ':', 1)
282 if not os.path.isfile(vbdparam):
283 raise VmError('Disk image does not exist: %s' %
284 vbdparam)
285 if name == 'vif':
286 type = sxp.child_value(info, 'type')
287 if type != 'ioemu':
288 continue
289 nics += 1
290 mac = sxp.child_value(info, 'mac')
291 if mac == None:
292 mac = randomMAC()
293 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
294 model = sxp.child_value(info, 'model', 'rtl8139')
295 ret.append("-net")
296 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
297 (nics, mac, model))
298 ret.append("-net")
299 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
300 if name == 'vtpm':
301 instance = sxp.child_value(info, 'pref_instance')
302 ret.append("-instance")
303 ret.append("%s" % instance)
304 return ret
306 def configVNC(self, config):
307 # Handle graphics library related options
308 vnc = sxp.child_value(config, 'vnc')
309 vncdisplay = sxp.child_value(config, 'vncdisplay',
310 int(self.vm.getDomid()))
311 sdl = sxp.child_value(config, 'sdl')
312 ret = []
313 nographic = sxp.child_value(config, 'nographic')
314 if nographic:
315 ret.append('-nographic')
316 return ret
317 if vnc:
318 ret = ret + ['-vnc', '%d' % vncdisplay, '-k', 'en-us']
319 return ret
321 def createDeviceModel(self):
322 if self.pid:
323 return
324 # Execute device model.
325 #todo: Error handling
326 args = [self.device_model]
327 args = args + ([ "-d", "%d" % self.vm.getDomid(),
328 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
329 args = args + self.dmargs
330 env = dict(os.environ)
331 if self.display:
332 env['DISPLAY'] = self.display
333 if self.xauthority:
334 env['XAUTHORITY'] = self.xauthority
335 if self.vncconsole:
336 args = args + ([ "-vncviewer" ])
337 log.info("spawning device models: %s %s", self.device_model, args)
338 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
339 log.info("device model pid: %d", self.pid)
341 def destroy(self):
342 self.unregister_shutdown_watch();
343 import signal
344 if not self.pid:
345 return
346 os.kill(self.pid, signal.SIGKILL)
347 os.waitpid(self.pid, 0)
348 self.pid = 0
350 def getDomainMemory(self, mem_kb):
351 """@see ImageHandler.getDomainMemory"""
352 if os.uname()[4] == 'ia64':
353 page_kb = 16
354 # ROM size for guest firmware, ioreq page and xenstore page
355 extra_pages = 1024 + 2
356 else:
357 page_kb = 4
358 # This was derived emperically:
359 # 2.4 MB overhead per 1024 MB RAM + 8 MB constant
360 # + 4 to avoid low-memory condition
361 extra_mb = (2.4/1024) * (mem_kb/1024.0) + 12;
362 extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
363 return mem_kb + extra_pages * page_kb
365 def register_shutdown_watch(self):
366 """ add xen store watch on control/shutdown """
367 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
368 self.hvm_shutdown)
369 log.debug("hvm shutdown watch registered")
371 def unregister_shutdown_watch(self):
372 """Remove the watch on the control/shutdown, if any. Nothrow
373 guarantee."""
375 try:
376 if self.shutdownWatch:
377 self.shutdownWatch.unwatch()
378 except:
379 log.exception("Unwatching hvm shutdown watch failed.")
380 self.shutdownWatch = None
381 log.debug("hvm shutdown watch unregistered")
383 def hvm_shutdown(self, _):
384 """ watch call back on node control/shutdown,
385 if node changed, this function will be called
386 """
387 from xen.xend.XendDomainInfo import shutdown_reasons
388 xd = xen.xend.XendDomain.instance()
389 vm = xd.domain_lookup( self.vm.getDomid() )
391 reason = vm.readDom('control/shutdown')
392 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
393 for x in shutdown_reasons.keys():
394 if shutdown_reasons[x] == reason:
395 vm.info['shutdown'] = 1
396 vm.info['shutdown_reason'] = x
397 vm.refreshShutdown(vm.info)
399 return 1 # Keep watching
401 """Table of image handler classes for virtual machine images. Indexed by
402 image type.
403 """
404 imageHandlerClasses = {}
407 for h in LinuxImageHandler, HVMImageHandler:
408 imageHandlerClasses[h.ostype] = h
411 def findImageHandlerClass(image):
412 """Find the image handler class for an image config.
414 @param image config
415 @return ImageHandler subclass or None
416 """
417 ty = sxp.name(image)
418 if ty is None:
419 raise VmError('missing image type')
420 imageClass = imageHandlerClasses.get(ty)
421 if imageClass is None:
422 raise VmError('unknown image type: ' + ty)
423 return imageClass