ia64/xen-unstable

view tools/python/xen/xend/image.py @ 7014:0f71667deb52

VMX guest memory calculation in python code is corrupted by changeset
6984, this patch fixes it.

Also print vmx builder parameters before calling vmx builer, just like
what's done before calling linux builder.

With this patch, qemu dm using SDL can work, however qemu dm using VNC
still has bug, we will fix it soon.

Signed-off-by: Xin Li <xin.b.li@intel.com>
author emellor@ewan
date Thu Sep 22 11:21:37 2005 +0100 (2005-09-22)
parents 11cca45a9a79
children eba5afe9aa37 10759a44ce3b
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
23 import xen.lowlevel.xc
24 from xen.xend import sxp
25 from xen.xend.XendError import VmError
26 from xen.xend.XendLogging import log
28 from xen.xend.server import channel
31 xc = xen.lowlevel.xc.new()
34 MAX_GUEST_CMDLINE = 1024
36 class ImageHandler:
37 """Abstract base class for image handlers.
39 initDomain() is called to initialise the domain memory and parse
40 the configuration.
42 createImage() is called to configure and build the domain from its
43 kernel image and ramdisk etc.
45 The method buildDomain() is used to build the domain, and must be
46 defined in a subclass. Usually this is the only method that needs
47 defining in a subclass.
49 The method createDeviceModel() is called to create the domain device
50 model if it needs one. The default is to do nothing.
52 The method destroy() is called when the domain is destroyed.
53 The default is to do nothing.
55 """
57 #======================================================================
58 # Class vars and methods.
60 """Table of image handler classes for virtual machine images.
61 Indexed by image type.
62 """
63 imageHandlerClasses = {}
65 def addImageHandlerClass(cls, h):
66 """Add a handler class for an image type
67 @param h: handler: ImageHandler subclass
68 """
69 cls.imageHandlerClasses[h.ostype] = h
71 addImageHandlerClass = classmethod(addImageHandlerClass)
73 def findImageHandlerClass(cls, image):
74 """Find the image handler class for an image config.
76 @param image config
77 @return ImageHandler subclass or None
78 """
79 ty = sxp.name(image)
80 if ty is None:
81 raise VmError('missing image type')
82 imageClass = cls.imageHandlerClasses.get(ty)
83 if imageClass is None:
84 raise VmError('unknown image type: ' + ty)
85 return imageClass
87 findImageHandlerClass = classmethod(findImageHandlerClass)
89 def create(cls, vm, imageConfig, deviceConfig):
90 """Create an image handler for a vm.
92 @return ImageHandler instance
93 """
94 imageClass = cls.findImageHandlerClass(imageConfig)
95 return imageClass(vm, imageConfig, deviceConfig)
97 create = classmethod(create)
99 #======================================================================
100 # Instance vars and methods.
102 ostype = None
104 kernel = None
105 ramdisk = None
106 cmdline = None
108 flags = 0
110 def __init__(self, vm, imageConfig, deviceConfig):
111 self.vm = vm
112 self.configure(imageConfig, deviceConfig)
114 def configure(self, imageConfig, _):
115 """Config actions common to all unix-like domains."""
117 self.kernel = sxp.child_value(imageConfig, "kernel")
118 self.cmdline = ""
119 ip = sxp.child_value(imageConfig, "ip", None)
120 if ip:
121 self.cmdline += " ip=" + ip
122 root = sxp.child_value(imageConfig, "root")
123 if root:
124 self.cmdline += " root=" + root
125 args = sxp.child_value(imageConfig, "args")
126 if args:
127 self.cmdline += " " + args
128 self.ramdisk = sxp.child_value(imageConfig, "ramdisk", '')
130 self.vm.storeVm(("image/ostype", self.ostype),
131 ("image/kernel", self.kernel),
132 ("image/cmdline", self.cmdline),
133 ("image/ramdisk", self.ramdisk))
135 def unlink(self, f):
136 if not f: return
137 try:
138 os.unlink(f)
139 except OSError, ex:
140 log.warning("error removing bootloader file '%s': %s", f, ex)
142 def initDomain(self, dom, memory, ssidref, cpu, cpu_weight, bootloading):
143 """Initial domain create.
145 @param memory In KiB
146 @return domain id
147 """
149 mem_kb = self.getDomainMemory(memory)
150 dom = xc.domain_create(dom = dom or 0, ssidref = ssidref)
151 # if bootloader, unlink here. But should go after buildDomain() ?
152 if bootloading:
153 self.unlink(self.kernel)
154 self.unlink(self.ramdisk)
155 if dom <= 0:
156 raise VmError('Creating domain failed: name=%s' %
157 self.vm.getName())
158 if cpu is None:
159 cpu = -1;
160 log.debug("initDomain: cpu=%d mem_kb=%d ssidref=%d dom=%d", cpu, mem_kb, ssidref, dom)
161 xc.domain_setcpuweight(dom, cpu_weight)
162 xc.domain_setmaxmem(dom, mem_kb)
164 try:
165 xc.domain_memory_increase_reservation(dom, mem_kb, 0, 0)
166 except:
167 xc.domain_destroy(dom)
168 raise
170 if cpu != -1:
171 xc.domain_pincpu(dom, 0, 1<<int(cpu))
172 return dom
174 def createImage(self):
175 """Entry point to create domain memory image.
176 Override in subclass if needed.
177 """
178 self.createDomain()
180 def createDomain(self):
181 """Build the domain boot image.
182 """
183 # Set params and call buildDomain().
184 self.flags = self.vm.getBackendFlags()
186 if not os.path.isfile(self.kernel):
187 raise VmError('Kernel image does not exist: %s' % self.kernel)
188 if self.ramdisk and not os.path.isfile(self.ramdisk):
189 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
190 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
191 log.warning('kernel cmdline too long, domain %d',
192 self.vm.getDomain())
194 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
195 self.vm.getDomain(), self.vm.getVCpuCount())
196 err = self.buildDomain()
197 if err != 0:
198 raise VmError('Building domain failed: ostype=%s dom=%d err=%d'
199 % (self.ostype, self.vm.getDomain(), err))
201 def getDomainMemory(self, mem):
202 """@return The memory required, in KiB, by the domain to store the
203 given amount, also in KiB. This is normally just mem, but VMX domains
204 have overheads to account for."""
205 return mem
207 def buildDomain(self):
208 """Build the domain. Define in subclass."""
209 raise NotImplementedError()
211 def createDeviceModel(self):
212 """Create device model for the domain (define in subclass if needed)."""
213 pass
215 def destroy(self):
216 """Extra cleanup on domain destroy (define in subclass if needed)."""
217 pass
219 def set_vminfo(self, d):
220 if d.has_key('store_mfn'):
221 self.vm.setStoreRef(d.get('store_mfn'))
222 if d.has_key('console_mfn'):
223 self.vm.setConsoleRef(d.get('console_mfn'))
225 addImageHandlerClass = ImageHandler.addImageHandlerClass
227 class LinuxImageHandler(ImageHandler):
229 ostype = "linux"
231 def buildDomain(self):
232 if self.vm.store_channel:
233 store_evtchn = self.vm.store_channel.port2
234 else:
235 store_evtchn = 0
236 if self.vm.console_channel:
237 console_evtchn = self.vm.console_channel.port2
238 else:
239 console_evtchn = 0
241 log.debug("dom = %d", self.vm.getDomain())
242 log.debug("image = %s", self.kernel)
243 log.debug("store_evtchn = %d", store_evtchn)
244 log.debug("console_evtchn = %d", console_evtchn)
245 log.debug("cmdline = %s", self.cmdline)
246 log.debug("ramdisk = %s", self.ramdisk)
247 log.debug("flags = %d", self.flags)
248 log.debug("vcpus = %d", self.vm.getVCpuCount())
250 ret = xc.linux_build(dom = self.vm.getDomain(),
251 image = self.kernel,
252 store_evtchn = store_evtchn,
253 console_evtchn = console_evtchn,
254 cmdline = self.cmdline,
255 ramdisk = self.ramdisk,
256 flags = self.flags,
257 vcpus = self.vm.getVCpuCount())
258 if isinstance(ret, dict):
259 self.set_vminfo(ret)
260 return 0
261 return ret
263 class VmxImageHandler(ImageHandler):
265 ostype = "vmx"
267 def configure(self, imageConfig, deviceConfig):
268 ImageHandler.configure(self, imageConfig, deviceConfig)
270 self.memmap = sxp.child_value(imageConfig, 'memmap')
271 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
272 self.device_model = sxp.child_value(imageConfig, 'device_model')
273 if not self.device_model:
274 raise VmError("vmx: missing device model")
275 self.display = sxp.child_value(imageConfig, 'display')
277 self.vm.storeVm(("image/memmap", self.memmap),
278 ("image/dmargs", " ".join(self.dmargs)),
279 ("image/device-model", self.device_model),
280 ("image/display", self.display))
282 self.device_channel = None
283 self.pid = 0
284 self.memmap_value = []
286 self.dmargs += self.configVNC(imageConfig)
289 def createImage(self):
290 """Create a VM for the VMX environment.
291 """
292 self.parseMemmap()
293 self.createDomain()
295 def buildDomain(self):
296 # Create an event channel
297 self.device_channel = channel.eventChannel(0, self.vm.getDomain())
298 log.info("VMX device model port: %d", self.device_channel.port2)
299 if self.vm.store_channel:
300 store_evtchn = self.vm.store_channel.port2
301 else:
302 store_evtchn = 0
304 log.debug("dom = %d", self.vm.getDomain())
305 log.debug("image = %s", self.kernel)
306 log.debug("control_evtchn = %d", self.device_channel.port2)
307 log.debug("store_evtchn = %d", store_evtchn)
308 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
309 log.debug("memmap = %s", self.memmap_value)
310 log.debug("cmdline = %s", self.cmdline)
311 log.debug("ramdisk = %s", self.ramdisk)
312 log.debug("flags = %d", self.flags)
313 log.debug("vcpus = %d", self.vm.getVCpuCount())
315 ret = xc.vmx_build(dom = self.vm.getDomain(),
316 image = self.kernel,
317 control_evtchn = self.device_channel.port2,
318 store_evtchn = store_evtchn,
319 memsize = self.vm.getMemoryTarget() / 1024,
320 memmap = self.memmap_value,
321 cmdline = self.cmdline,
322 ramdisk = self.ramdisk,
323 flags = self.flags,
324 vcpus = self.vm.getVCpuCount())
325 if isinstance(ret, dict):
326 self.set_vminfo(ret)
327 return 0
328 return ret
330 def parseMemmap(self):
331 if self.memmap is None:
332 return
333 memmap = sxp.parse(open(self.memmap))[0]
334 from xen.util.memmap import memmap_parse
335 self.memmap_value = memmap_parse(memmap)
337 # Return a list of cmd line args to the device models based on the
338 # xm config file
339 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
340 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb',
341 'localtime', 'serial', 'stdvga', 'isa', 'vcpus' ]
342 ret = []
343 for a in dmargs:
344 v = sxp.child_value(imageConfig, a)
346 # python doesn't allow '-' in variable names
347 if a == 'stdvga': a = 'std-vga'
349 # Handle booleans gracefully
350 if a in ['localtime', 'std-vga', 'isa']:
351 if v != None: v = int(v)
353 log.debug("args: %s, val: %s" % (a,v))
354 if v:
355 ret.append("-%s" % a)
356 ret.append("%s" % v)
358 # Handle disk/network related options
359 for (name, info) in deviceConfig:
360 if name == 'vbd':
361 uname = sxp.child_value(info, 'uname')
362 typedev = sxp.child_value(info, 'dev')
363 (_, vbdparam) = string.split(uname, ':', 1)
364 if re.match('^ioemu:', typedev):
365 (emtype, vbddev) = string.split(typedev, ':', 1)
366 else:
367 emtype = 'vbd'
368 vbddev = typedev
369 if emtype != 'ioemu':
370 continue;
371 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
372 if vbddev not in vbddev_list:
373 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
374 ret.append("-%s" % vbddev)
375 ret.append("%s" % vbdparam)
376 if name == 'vif':
377 mac = sxp.child_value(info, 'mac')
378 ret.append("-macaddr")
379 ret.append("%s" % mac)
380 if name == 'vtpm':
381 instance = sxp.child_value(info, 'instance')
382 ret.append("-instance")
383 ret.append("%s" % instance)
384 return ret
386 def configVNC(self, config):
387 # Handle graphics library related options
388 vnc = sxp.child_value(config, 'vnc')
389 sdl = sxp.child_value(config, 'sdl')
390 ret = []
391 nographic = sxp.child_value(config, 'nographic')
392 if nographic:
393 ret.append('-nographic')
394 return ret
396 if vnc and sdl:
397 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
398 elif vnc:
399 ret = ret + ['-vnc', '-k', 'en-us']
400 if vnc:
401 vncport = int(self.vm.getDomain()) + 5900
402 ret = ret + ['-vncport', '%d' % vncport]
403 return ret
405 def createDeviceModel(self):
406 if self.pid:
407 return
408 # Execute device model.
409 #todo: Error handling
410 # XXX RN: note that the order of args matter!
411 args = [self.device_model]
412 vnc = self.vncParams()
413 if len(vnc):
414 args = args + vnc
415 args = args + ([ "-d", "%d" % self.vm.getDomain(),
416 "-p", "%d" % self.device_channel.port1,
417 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
418 args = args + self.dmargs
419 env = dict(os.environ)
420 env['DISPLAY'] = self.display
421 log.info("spawning device models: %s %s", self.device_model, args)
422 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
423 log.info("device model pid: %d", self.pid)
425 def vncParams(self):
426 # see if a vncviewer was specified
427 # XXX RN: bit of a hack. should unify this, maybe stick in config space
428 vncconnect=[]
429 args = self.cmdline
430 if args:
431 arg_list = string.split(args)
432 for arg in arg_list:
433 al = string.split(arg, '=')
434 if al[0] == "VNC_VIEWER":
435 vncconnect=["-vncconnect", "%s" % al[1]]
436 break
437 return vncconnect
439 def destroy(self):
440 channel.eventChannelClose(self.device_channel)
441 import signal
442 if not self.pid:
443 return
444 os.kill(self.pid, signal.SIGKILL)
445 os.waitpid(self.pid, 0)
446 self.pid = 0
448 def getDomainMemory(self, mem):
449 """@see ImageHandler.getDomainMemory"""
450 # for ioreq_t and xenstore
451 static_pages = 2
452 return mem + self.getPageTableSize(mem / 1024) + 4 * static_pages
454 def getPageTableSize(self, mem_mb):
455 """Return the size of memory needed for 1:1 page tables for physical
456 mode.
458 @param mem_mb: size in MB
459 @return size in KB
460 """
461 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
462 if os.uname()[4] == 'x86_64':
463 return (5 + ((mem_mb + 1) >> 1)) * 4
464 elif os.uname()[4] == 'ia64':
465 # XEN/IA64 has p2m table allocated on demand, so only return
466 # guest firmware size here.
467 return 16 * 1024
468 else:
469 return (1 + ((mem_mb + 3) >> 2)) * 4