ia64/xen-unstable

view tools/python/xen/xend/image.py @ 10733:6d8f2d78d7c8

Update xend to support network configuration for qemu 0.8.1 based ioemu.
Remove the ne2000 option, the network device type can now be selected
on a per-device basis by adding a model= property to the device's entry
in the vif list.

Signed-off-by: Christian Limpach <Christian.Limpach@xensource.com>
author chris@kneesaa.uk.xensource.com
date Wed Jul 12 19:16:12 2006 +0100 (2006-07-12)
parents e2a2b2da92f4
children a70c4f9657cc
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')
212 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
213 ("image/device-model", self.device_model),
214 ("image/display", self.display))
216 self.pid = 0
218 self.dmargs += self.configVNC(imageConfig)
220 self.pae = int(sxp.child_value(imageConfig, 'pae', 0))
222 self.acpi = int(sxp.child_value(imageConfig, 'acpi', 0))
223 self.apic = int(sxp.child_value(imageConfig, 'apic', 0))
225 def buildDomain(self):
226 store_evtchn = self.vm.getStorePort()
228 log.debug("dom = %d", self.vm.getDomid())
229 log.debug("image = %s", self.kernel)
230 log.debug("store_evtchn = %d", store_evtchn)
231 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
232 log.debug("vcpus = %d", self.vm.getVCpuCount())
233 log.debug("pae = %d", self.pae)
234 log.debug("acpi = %d", self.acpi)
235 log.debug("apic = %d", self.apic)
237 self.register_shutdown_watch()
239 return xc.hvm_build(dom = self.vm.getDomid(),
240 image = self.kernel,
241 store_evtchn = store_evtchn,
242 memsize = self.vm.getMemoryTarget() / 1024,
243 vcpus = self.vm.getVCpuCount(),
244 pae = self.pae,
245 acpi = self.acpi,
246 apic = self.apic)
248 # Return a list of cmd line args to the device models based on the
249 # xm config file
250 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
251 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb', 'audio',
252 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
253 'usb', 'usbdevice']
254 ret = []
255 for a in dmargs:
256 v = sxp.child_value(imageConfig, a)
258 # python doesn't allow '-' in variable names
259 if a == 'stdvga': a = 'std-vga'
260 if a == 'audio': a = 'enable-audio'
262 # Handle booleans gracefully
263 if a in ['localtime', 'std-vga', 'isa', 'enable-audio', 'usb']:
264 if v != None: v = int(v)
265 if v: ret.append("-%s" % a)
266 else:
267 if v:
268 ret.append("-%s" % a)
269 ret.append("%s" % v)
270 log.debug("args: %s, val: %s" % (a,v))
272 # Handle disk/network related options
273 mac = None
274 ret = ret + ["-domain-name", "%s" % self.vm.info['name']]
275 nics = 0
276 for (name, info) in deviceConfig:
277 if name == 'vbd':
278 uname = sxp.child_value(info, 'uname')
279 typedev = sxp.child_value(info, 'dev')
280 (_, vbdparam) = string.split(uname, ':', 1)
282 if 'file:' in uname and not os.path.isfile(vbdparam):
283 raise VmError('Disk image does not exist: %s' % vbdparam)
285 if 'ioemu:' in typedev:
286 (emtype, vbddev) = string.split(typedev, ':', 1)
287 else:
288 emtype = 'vbd'
289 vbddev = typedev
290 if emtype == 'vbd':
291 continue;
292 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
293 if vbddev not in vbddev_list:
294 raise VmError("hvm: for qemu vbd type=file&dev=hda~hdd")
295 ret.append("-%s" % vbddev)
296 ret.append("%s" % vbdparam)
297 if name == 'vif':
298 type = sxp.child_value(info, 'type')
299 if type != 'ioemu':
300 continue
301 nics += 1
302 mac = sxp.child_value(info, 'mac')
303 if mac == None:
304 mac = randomMAC()
305 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
306 model = sxp.child_value(info, 'model', 'rtl8139')
307 ret.append("-net")
308 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
309 (nics, mac, model))
310 ret.append("-net")
311 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
312 if name == 'vtpm':
313 instance = sxp.child_value(info, 'pref_instance')
314 ret.append("-instance")
315 ret.append("%s" % instance)
316 return ret
318 def configVNC(self, config):
319 # Handle graphics library related options
320 vnc = sxp.child_value(config, 'vnc')
321 sdl = sxp.child_value(config, 'sdl')
322 ret = []
323 nographic = sxp.child_value(config, 'nographic')
324 if nographic:
325 ret.append('-nographic')
326 return ret
328 if vnc and sdl:
329 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
330 elif vnc:
331 ret = ret + ['-vnc', '-k', 'en-us']
332 if vnc:
333 vncport = int(self.vm.getDomid()) + 5900
334 ret = ret + ['-vncport', '%d' % vncport]
335 return ret
337 def createDeviceModel(self):
338 if self.pid:
339 return
340 # Execute device model.
341 #todo: Error handling
342 # XXX RN: note that the order of args matter!
343 args = [self.device_model]
344 vnc = self.vncParams()
345 if len(vnc):
346 args = args + vnc
347 args = args + ([ "-d", "%d" % self.vm.getDomid(),
348 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
349 args = args + self.dmargs
350 env = dict(os.environ)
351 if self.display:
352 env['DISPLAY'] = self.display
353 if self.xauthority:
354 env['XAUTHORITY'] = self.xauthority
355 log.info("spawning device models: %s %s", self.device_model, args)
356 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
357 log.info("device model pid: %d", self.pid)
359 def vncParams(self):
360 # see if a vncviewer was specified
361 # XXX RN: bit of a hack. should unify this, maybe stick in config space
362 vncconnect=[]
363 args = self.cmdline
364 if args:
365 arg_list = string.split(args)
366 for arg in arg_list:
367 al = string.split(arg, '=')
368 if al[0] == "VNC_VIEWER":
369 vncconnect=["-vncconnect", "%s" % al[1]]
370 break
371 return vncconnect
373 def destroy(self):
374 self.unregister_shutdown_watch();
375 import signal
376 if not self.pid:
377 return
378 os.kill(self.pid, signal.SIGKILL)
379 os.waitpid(self.pid, 0)
380 self.pid = 0
382 def getDomainMemory(self, mem_kb):
383 """@see ImageHandler.getDomainMemory"""
384 if os.uname()[4] == 'ia64':
385 page_kb = 16
386 # ROM size for guest firmware, ioreq page and xenstore page
387 extra_pages = 1024 + 2
388 else:
389 page_kb = 4
390 # This was derived emperically:
391 # 2.4 MB overhead per 1024 MB RAM + 8 MB constant
392 # + 4 to avoid low-memory condition
393 extra_mb = (2.4/1024) * (mem_kb/1024.0) + 12;
394 extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
395 return mem_kb + extra_pages * page_kb
397 def register_shutdown_watch(self):
398 """ add xen store watch on control/shutdown """
399 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
400 self.hvm_shutdown)
401 log.debug("hvm shutdown watch registered")
403 def unregister_shutdown_watch(self):
404 """Remove the watch on the control/shutdown, if any. Nothrow
405 guarantee."""
407 try:
408 if self.shutdownWatch:
409 self.shutdownWatch.unwatch()
410 except:
411 log.exception("Unwatching hvm shutdown watch failed.")
412 self.shutdownWatch = None
413 log.debug("hvm shutdown watch unregistered")
415 def hvm_shutdown(self, _):
416 """ watch call back on node control/shutdown,
417 if node changed, this function will be called
418 """
419 from xen.xend.XendDomainInfo import shutdown_reasons
420 xd = xen.xend.XendDomain.instance()
421 vm = xd.domain_lookup( self.vm.getDomid() )
423 reason = vm.readDom('control/shutdown')
424 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
425 for x in shutdown_reasons.keys():
426 if shutdown_reasons[x] == reason:
427 vm.info['shutdown'] = 1
428 vm.info['shutdown_reason'] = x
429 vm.refreshShutdown(vm.info)
431 return 1 # Keep watching
433 """Table of image handler classes for virtual machine images. Indexed by
434 image type.
435 """
436 imageHandlerClasses = {}
439 for h in LinuxImageHandler, HVMImageHandler:
440 imageHandlerClasses[h.ostype] = h
443 def findImageHandlerClass(image):
444 """Find the image handler class for an image config.
446 @param image config
447 @return ImageHandler subclass or None
448 """
449 ty = sxp.name(image)
450 if ty is None:
451 raise VmError('missing image type')
452 imageClass = imageHandlerClasses.get(ty)
453 if imageClass is None:
454 raise VmError('unknown image type: ' + ty)
455 return imageClass