ia64/xen-unstable

view tools/python/xen/xend/image.py @ 11029:bd04004865ba

[qemu] Add vncunused option.
If the port used for the requested display number is in use, try
additional ports until a free port is found.

Signed-off-by: Christian Limpach <Christian.Limpach@xensource.com>
author chris@kneesaa.uk.xensource.com
date Wed Aug 09 15:03:38 2006 +0100 (2006-08-09)
parents d25efa45355b
children cfd9d4601022
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', 'soundhw',
253 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
254 'acpi', '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'
262 # Handle booleans gracefully
263 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
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 if 'file:' in uname:
280 (_, vbdparam) = string.split(uname, ':', 1)
281 if not os.path.isfile(vbdparam):
282 raise VmError('Disk image does not exist: %s' %
283 vbdparam)
284 if name == 'vif':
285 type = sxp.child_value(info, 'type')
286 if type != 'ioemu':
287 continue
288 nics += 1
289 mac = sxp.child_value(info, 'mac')
290 if mac == None:
291 mac = randomMAC()
292 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
293 model = sxp.child_value(info, 'model', 'rtl8139')
294 ret.append("-net")
295 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
296 (nics, mac, model))
297 ret.append("-net")
298 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
299 if name == 'vtpm':
300 instance = sxp.child_value(info, 'pref_instance')
301 ret.append("-instance")
302 ret.append("%s" % instance)
303 return ret
305 def configVNC(self, config):
306 # Handle graphics library related options
307 vnc = sxp.child_value(config, 'vnc')
308 vncdisplay = sxp.child_value(config, 'vncdisplay',
309 int(self.vm.getDomid()))
310 vncunused = sxp.child_value(config, 'vncunused')
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 if vncunused:
320 ret += ['-vncunused']
321 return ret
323 def createDeviceModel(self):
324 if self.pid:
325 return
326 # Execute device model.
327 #todo: Error handling
328 args = [self.device_model]
329 args = args + ([ "-d", "%d" % self.vm.getDomid(),
330 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
331 args = args + self.dmargs
332 env = dict(os.environ)
333 if self.display:
334 env['DISPLAY'] = self.display
335 if self.xauthority:
336 env['XAUTHORITY'] = self.xauthority
337 if self.vncconsole:
338 args = args + ([ "-vncviewer" ])
339 log.info("spawning device models: %s %s", self.device_model, args)
340 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
341 log.info("device model pid: %d", self.pid)
343 def destroy(self):
344 self.unregister_shutdown_watch();
345 import signal
346 if not self.pid:
347 return
348 os.kill(self.pid, signal.SIGKILL)
349 os.waitpid(self.pid, 0)
350 self.pid = 0
352 def getDomainMemory(self, mem_kb):
353 """@see ImageHandler.getDomainMemory"""
354 if os.uname()[4] == 'ia64':
355 page_kb = 16
356 # ROM size for guest firmware, ioreq page and xenstore page
357 extra_pages = 1024 + 2
358 else:
359 page_kb = 4
360 # This was derived emperically:
361 # 2.4 MB overhead per 1024 MB RAM + 8 MB constant
362 # + 4 to avoid low-memory condition
363 extra_mb = (2.4/1024) * (mem_kb/1024.0) + 12;
364 extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
365 return mem_kb + extra_pages * page_kb
367 def register_shutdown_watch(self):
368 """ add xen store watch on control/shutdown """
369 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
370 self.hvm_shutdown)
371 log.debug("hvm shutdown watch registered")
373 def unregister_shutdown_watch(self):
374 """Remove the watch on the control/shutdown, if any. Nothrow
375 guarantee."""
377 try:
378 if self.shutdownWatch:
379 self.shutdownWatch.unwatch()
380 except:
381 log.exception("Unwatching hvm shutdown watch failed.")
382 self.shutdownWatch = None
383 log.debug("hvm shutdown watch unregistered")
385 def hvm_shutdown(self, _):
386 """ watch call back on node control/shutdown,
387 if node changed, this function will be called
388 """
389 from xen.xend.XendDomainInfo import shutdown_reasons
390 xd = xen.xend.XendDomain.instance()
391 vm = xd.domain_lookup( self.vm.getDomid() )
393 reason = vm.readDom('control/shutdown')
394 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
395 for x in shutdown_reasons.keys():
396 if shutdown_reasons[x] == reason:
397 vm.info['shutdown'] = 1
398 vm.info['shutdown_reason'] = x
399 vm.refreshShutdown(vm.info)
401 return 1 # Keep watching
403 """Table of image handler classes for virtual machine images. Indexed by
404 image type.
405 """
406 imageHandlerClasses = {}
409 for h in LinuxImageHandler, HVMImageHandler:
410 imageHandlerClasses[h.ostype] = h
413 def findImageHandlerClass(image):
414 """Find the image handler class for an image config.
416 @param image config
417 @return ImageHandler subclass or None
418 """
419 ty = sxp.name(image)
420 if ty is None:
421 raise VmError('missing image type')
422 imageClass = imageHandlerClasses.get(ty)
423 if imageClass is None:
424 raise VmError('unknown image type: ' + ty)
425 return imageClass