ia64/xen-unstable

view tools/python/xen/xend/image.py @ 11733:ddc56007bf3f

[XEND] If 'vncused' is set, it won't specify an explicit display num to QEMU.

In the current xen-unstable.hg tree tough, XenD will always pass an
explicit '-vnc <display num>' parameter to qemu-dm, regardless of
whether 'vncunused' is set. So the 'vncunused' bit only takes effect
if the explicitly passed display already has something bound to it.

Consider what happens when the host machine has been up for a while -
the Domain ID's start getting very high. This plays havoc with
firewalls - for example an admin may know that only 10 domains will
ever be running at any time, so they should be able to simply open up
ports 5900 -> 5910.

The correct behaviour should be:

- If 'vncdisplay' is set, use that explicit display
- If 'vncunused' is set non-zero, allocate first port about 5900
- Allocate fixed port based on domain-ID

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
author kaf24@firebug.cl.cam.ac.uk
date Wed Oct 04 09:42:41 2006 +0100 (2006-10-04)
parents 02311d8aba86
children a95dfbc8dca8
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
30 from xen.xend import arch
31 from xen.xend import FlatDeviceTree
34 xc = xen.lowlevel.xc.xc()
37 MAX_GUEST_CMDLINE = 1024
40 def create(vm, imageConfig, deviceConfig):
41 """Create an image handler for a vm.
43 @return ImageHandler instance
44 """
45 return findImageHandlerClass(imageConfig)(vm, imageConfig, deviceConfig)
48 class ImageHandler:
49 """Abstract base class for image handlers.
51 createImage() is called to configure and build the domain from its
52 kernel image and ramdisk etc.
54 The method buildDomain() is used to build the domain, and must be
55 defined in a subclass. Usually this is the only method that needs
56 defining in a subclass.
58 The method createDeviceModel() is called to create the domain device
59 model if it needs one. The default is to do nothing.
61 The method destroy() is called when the domain is destroyed.
62 The default is to do nothing.
63 """
65 ostype = None
68 def __init__(self, vm, imageConfig, deviceConfig):
69 self.vm = vm
71 self.kernel = None
72 self.ramdisk = None
73 self.cmdline = None
75 self.configure(imageConfig, deviceConfig)
77 def configure(self, imageConfig, _):
78 """Config actions common to all unix-like domains."""
80 def get_cfg(name, default = None):
81 return sxp.child_value(imageConfig, name, default)
83 self.kernel = get_cfg("kernel")
84 self.cmdline = ""
85 ip = get_cfg("ip")
86 if ip:
87 self.cmdline += " ip=" + ip
88 root = get_cfg("root")
89 if root:
90 self.cmdline += " root=" + root
91 args = get_cfg("args")
92 if args:
93 self.cmdline += " " + args
94 self.ramdisk = get_cfg("ramdisk", '')
96 self.vm.storeVm(("image/ostype", self.ostype),
97 ("image/kernel", self.kernel),
98 ("image/cmdline", self.cmdline),
99 ("image/ramdisk", self.ramdisk))
102 def cleanupBootloading(self):
103 self.unlink(self.kernel)
104 self.unlink(self.ramdisk)
107 def unlink(self, f):
108 if not f: return
109 try:
110 os.unlink(f)
111 except OSError, ex:
112 log.warning("error removing bootloader file '%s': %s", f, ex)
115 def createImage(self):
116 """Entry point to create domain memory image.
117 Override in subclass if needed.
118 """
119 return self.createDomain()
122 def createDomain(self):
123 """Build the domain boot image.
124 """
125 # Set params and call buildDomain().
127 if not os.path.isfile(self.kernel):
128 raise VmError('Kernel image does not exist: %s' % self.kernel)
129 if self.ramdisk and not os.path.isfile(self.ramdisk):
130 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
131 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
132 log.warning('kernel cmdline too long, domain %d',
133 self.vm.getDomid())
135 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
136 self.vm.getDomid(), self.vm.getVCpuCount())
138 result = self.buildDomain()
140 if isinstance(result, dict):
141 return result
142 else:
143 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
144 % (self.ostype, self.vm.getDomid(), str(result)))
146 def getRequiredAvailableMemory(self, mem_kb):
147 """@param mem_kb The configured maxmem or memory, in KiB.
148 @return The corresponding required amount of memory for the domain,
149 also in KiB. This is normally the given mem_kb, but architecture- or
150 image-specific code may override this to add headroom where
151 necessary."""
152 return mem_kb
154 def getRequiredInitialReservation(self, mem_kb):
155 """@param mem_kb The configured memory, in KiB.
156 @return The corresponding required amount of memory to be free, also
157 in KiB. This is normally the same as getRequiredAvailableMemory, but
158 architecture- or image-specific code may override this to
159 add headroom where necessary."""
160 return self.getRequiredAvailableMemory(mem_kb)
162 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
163 """@param shadow_mem_kb The configured shadow memory, in KiB.
164 @param maxmem_kb The configured maxmem, in KiB.
165 @return The corresponding required amount of shadow memory, also in
166 KiB."""
167 # PV domains don't need any shadow memory
168 return 0
170 def buildDomain(self):
171 """Build the domain. Define in subclass."""
172 raise NotImplementedError()
174 def createDeviceModel(self):
175 """Create device model for the domain (define in subclass if needed)."""
176 pass
178 def destroy(self):
179 """Extra cleanup on domain destroy (define in subclass if needed)."""
180 pass
183 class LinuxImageHandler(ImageHandler):
185 ostype = "linux"
187 def buildDomain(self):
188 store_evtchn = self.vm.getStorePort()
189 console_evtchn = self.vm.getConsolePort()
191 log.debug("dom = %d", self.vm.getDomid())
192 log.debug("image = %s", self.kernel)
193 log.debug("store_evtchn = %d", store_evtchn)
194 log.debug("console_evtchn = %d", console_evtchn)
195 log.debug("cmdline = %s", self.cmdline)
196 log.debug("ramdisk = %s", self.ramdisk)
197 log.debug("vcpus = %d", self.vm.getVCpuCount())
198 log.debug("features = %s", self.vm.getFeatures())
200 return xc.linux_build(dom = self.vm.getDomid(),
201 image = self.kernel,
202 store_evtchn = store_evtchn,
203 console_evtchn = console_evtchn,
204 cmdline = self.cmdline,
205 ramdisk = self.ramdisk,
206 features = self.vm.getFeatures())
208 class PPC_LinuxImageHandler(LinuxImageHandler):
210 ostype = "linux"
212 def configure(self, imageConfig, deviceConfig):
213 LinuxImageHandler.configure(self, imageConfig, deviceConfig)
214 self.imageConfig = imageConfig
216 def buildDomain(self):
217 store_evtchn = self.vm.getStorePort()
218 console_evtchn = self.vm.getConsolePort()
220 log.debug("dom = %d", self.vm.getDomid())
221 log.debug("image = %s", self.kernel)
222 log.debug("store_evtchn = %d", store_evtchn)
223 log.debug("console_evtchn = %d", console_evtchn)
224 log.debug("cmdline = %s", self.cmdline)
225 log.debug("ramdisk = %s", self.ramdisk)
226 log.debug("vcpus = %d", self.vm.getVCpuCount())
227 log.debug("features = %s", self.vm.getFeatures())
229 devtree = FlatDeviceTree.build(self)
231 return xc.linux_build(dom = self.vm.getDomid(),
232 image = self.kernel,
233 store_evtchn = store_evtchn,
234 console_evtchn = console_evtchn,
235 cmdline = self.cmdline,
236 ramdisk = self.ramdisk,
237 features = self.vm.getFeatures(),
238 arch_args = devtree.to_bin())
240 class HVMImageHandler(ImageHandler):
242 def configure(self, imageConfig, deviceConfig):
243 ImageHandler.configure(self, imageConfig, deviceConfig)
245 info = xc.xeninfo()
246 if not 'hvm' in info['xen_caps']:
247 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
248 "supported by your CPU and enabled in your BIOS?")
250 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
251 self.device_model = sxp.child_value(imageConfig, 'device_model')
252 if not self.device_model:
253 raise VmError("hvm: missing device model")
254 self.display = sxp.child_value(imageConfig, 'display')
255 self.xauthority = sxp.child_value(imageConfig, 'xauthority')
256 self.vncconsole = sxp.child_value(imageConfig, 'vncconsole')
258 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
259 ("image/device-model", self.device_model),
260 ("image/display", self.display))
262 self.pid = 0
264 self.dmargs += self.configVNC(imageConfig)
266 self.pae = int(sxp.child_value(imageConfig, 'pae', 0))
268 self.acpi = int(sxp.child_value(imageConfig, 'acpi', 0))
269 self.apic = int(sxp.child_value(imageConfig, 'apic', 0))
271 def buildDomain(self):
272 store_evtchn = self.vm.getStorePort()
274 log.debug("dom = %d", self.vm.getDomid())
275 log.debug("image = %s", self.kernel)
276 log.debug("store_evtchn = %d", store_evtchn)
277 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
278 log.debug("vcpus = %d", self.vm.getVCpuCount())
279 log.debug("pae = %d", self.pae)
280 log.debug("acpi = %d", self.acpi)
281 log.debug("apic = %d", self.apic)
283 self.register_shutdown_watch()
285 return xc.hvm_build(dom = self.vm.getDomid(),
286 image = self.kernel,
287 store_evtchn = store_evtchn,
288 memsize = self.vm.getMemoryTarget() / 1024,
289 vcpus = self.vm.getVCpuCount(),
290 pae = self.pae,
291 acpi = self.acpi,
292 apic = self.apic)
294 # Return a list of cmd line args to the device models based on the
295 # xm config file
296 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
297 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
298 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
299 'acpi', 'usb', 'usbdevice']
300 ret = []
301 for a in dmargs:
302 v = sxp.child_value(imageConfig, a)
304 # python doesn't allow '-' in variable names
305 if a == 'stdvga': a = 'std-vga'
307 # Handle booleans gracefully
308 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
309 if v != None: v = int(v)
310 if v: ret.append("-%s" % a)
311 else:
312 if v:
313 ret.append("-%s" % a)
314 ret.append("%s" % v)
315 log.debug("args: %s, val: %s" % (a,v))
317 # Handle disk/network related options
318 mac = None
319 ret = ret + ["-domain-name", "%s" % self.vm.info['name']]
320 nics = 0
321 for (name, info) in deviceConfig:
322 if name == 'vbd':
323 uname = sxp.child_value(info, 'uname')
324 if uname is not None and 'file:' in uname:
325 (_, vbdparam) = string.split(uname, ':', 1)
326 if not os.path.isfile(vbdparam):
327 raise VmError('Disk image does not exist: %s' %
328 vbdparam)
329 if name == 'vif':
330 type = sxp.child_value(info, 'type')
331 if type != 'ioemu':
332 continue
333 nics += 1
334 mac = sxp.child_value(info, 'mac')
335 if mac == None:
336 mac = randomMAC()
337 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
338 model = sxp.child_value(info, 'model', 'rtl8139')
339 ret.append("-net")
340 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
341 (nics, mac, model))
342 ret.append("-net")
343 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
344 return ret
346 def configVNC(self, config):
347 # Handle graphics library related options
348 vnc = sxp.child_value(config, 'vnc')
349 sdl = sxp.child_value(config, 'sdl')
350 ret = []
351 nographic = sxp.child_value(config, 'nographic')
352 if nographic:
353 ret.append('-nographic')
354 return ret
355 if vnc:
356 vncdisplay = sxp.child_value(config, 'vncdisplay',
357 int(self.vm.getDomid()))
358 vncunused = sxp.child_value(config, 'vncunused')
359 if vncunused:
360 ret += ['-vncunused']
361 else:
362 ret += ['-vnc', '%d' % vncdisplay]
363 ret += ['-k', 'en-us']
364 return ret
366 def createDeviceModel(self):
367 if self.pid:
368 return
369 # Execute device model.
370 #todo: Error handling
371 args = [self.device_model]
372 args = args + ([ "-d", "%d" % self.vm.getDomid(),
373 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
374 args = args + self.dmargs
375 env = dict(os.environ)
376 if self.display:
377 env['DISPLAY'] = self.display
378 if self.xauthority:
379 env['XAUTHORITY'] = self.xauthority
380 if self.vncconsole:
381 args = args + ([ "-vncviewer" ])
382 log.info("spawning device models: %s %s", self.device_model, args)
383 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
384 log.info("device model pid: %d", self.pid)
386 def destroy(self):
387 self.unregister_shutdown_watch();
388 import signal
389 if not self.pid:
390 return
391 os.kill(self.pid, signal.SIGKILL)
392 os.waitpid(self.pid, 0)
393 self.pid = 0
395 def register_shutdown_watch(self):
396 """ add xen store watch on control/shutdown """
397 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
398 self.hvm_shutdown)
399 log.debug("hvm shutdown watch registered")
401 def unregister_shutdown_watch(self):
402 """Remove the watch on the control/shutdown, if any. Nothrow
403 guarantee."""
405 try:
406 if self.shutdownWatch:
407 self.shutdownWatch.unwatch()
408 except:
409 log.exception("Unwatching hvm shutdown watch failed.")
410 self.shutdownWatch = None
411 log.debug("hvm shutdown watch unregistered")
413 def hvm_shutdown(self, _):
414 """ watch call back on node control/shutdown,
415 if node changed, this function will be called
416 """
417 from xen.xend.XendDomainInfo import shutdown_reasons
418 xd = xen.xend.XendDomain.instance()
419 vm = xd.domain_lookup( self.vm.getDomid() )
421 reason = vm.readDom('control/shutdown')
422 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
423 for x in shutdown_reasons.keys():
424 if shutdown_reasons[x] == reason:
425 vm.info['shutdown'] = 1
426 vm.info['shutdown_reason'] = x
427 vm.refreshShutdown(vm.info)
429 return 1 # Keep watching
431 class IA64_HVM_ImageHandler(HVMImageHandler):
433 ostype = "hvm"
435 def getRequiredAvailableMemory(self, mem_kb):
436 page_kb = 16
437 # ROM size for guest firmware, ioreq page and xenstore page
438 extra_pages = 1024 + 2
439 return mem_kb + extra_pages * page_kb
441 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
442 # Explicit shadow memory is not a concept
443 return 0
445 class X86_HVM_ImageHandler(HVMImageHandler):
447 ostype = "hvm"
449 def getRequiredAvailableMemory(self, mem_kb):
450 # Add 8 MiB overhead for QEMU's video RAM.
451 return self.getRequiredInitialReservation(mem_kb) + 8192
453 def getRequiredInitialReservation(self, mem_kb):
454 page_kb = 4
455 # This was derived emperically:
456 # 2.4 MB overhead per 1024 MB RAM
457 # + 4 to avoid low-memory condition
458 extra_mb = (2.4/1024) * (mem_kb/1024.0) + 4;
459 extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
460 return mem_kb + extra_pages * page_kb
462 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
463 # The given value is the configured value -- we need to include the
464 # overhead due to getRequiredInitialReservation.
465 maxmem_kb = self.getRequiredInitialReservation(maxmem_kb)
467 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
468 # the minimum that Xen would allocate if no value were given.
469 return max(1024 * self.vm.getVCpuCount() + maxmem_kb / 256,
470 shadow_mem_kb)
473 _handlers = {
474 "powerpc": {
475 "linux": PPC_LinuxImageHandler,
476 },
477 "ia64": {
478 "linux": LinuxImageHandler,
479 "hvm": IA64_HVM_ImageHandler,
480 },
481 "x86": {
482 "linux": LinuxImageHandler,
483 "hvm": X86_HVM_ImageHandler,
484 },
485 }
487 def findImageHandlerClass(image):
488 """Find the image handler class for an image config.
490 @param image config
491 @return ImageHandler subclass or None
492 """
493 type = sxp.name(image)
494 if type is None:
495 raise VmError('missing image type')
496 try:
497 return _handlers[arch.type][type]
498 except KeyError:
499 raise VmError('unknown image type: ' + type)