ia64/xen-unstable

view tools/python/xen/xend/image.py @ 6700:12ff9c954ace

Give each domain some memory below 4GB. This solves the "PGD's must be below 4GB" for the initial page tables. I'm not sure we'll stick with this approach, but this is good enough for the time being.

PAE should be a *lot* more robust on systems that actually have more than 4GB thanks to all the various patches that went in today. I find it astounding that it ever appeared to work at all!

Signed-off-by: ian@xensource.com
author iap10@freefall.cl.cam.ac.uk
date Thu Sep 08 01:07:15 2005 +0000 (2005-09-08)
parents 7d0fb56b4a91
children 5db85ba1c4e0 3bde4219c681 aa0990ef260f
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 #============================================================================
18 import os, string
19 import re
21 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
22 from xen.xend import sxp
23 from xen.xend.XendError import VmError
24 from xen.xend.XendLogging import log
25 from xen.xend.xenstore import DBVar
27 from xen.xend.server import channel
29 """Flag for a block device backend domain."""
30 SIF_BLK_BE_DOMAIN = (1<<4)
32 """Flag for a net device backend domain."""
33 SIF_NET_BE_DOMAIN = (1<<5)
35 """Flag for a TPM device backend domain."""
36 SIF_TPM_BE_DOMAIN = (1<<7)
38 class ImageHandler:
39 """Abstract base class for image handlers.
41 initDomain() is called to initialise the domain memory.
43 createImage() is called to configure and build the domain from its
44 kernel image and ramdisk etc.
46 The method buildDomain() is used to build the domain, and must be
47 defined in a subclass. Usually this is the only method that needs
48 defining in a subclass.
50 The method createDeviceModel() is called to create the domain device
51 model if it needs one. The default is to do nothing.
53 The method destroy() is called when the domain is destroyed.
54 The default is to do nothing.
56 """
58 #======================================================================
59 # Class vars and methods.
61 """Table of image handler classes for virtual machine images.
62 Indexed by image type.
63 """
64 imageHandlerClasses = {}
66 def addImageHandlerClass(cls, h):
67 """Add a handler class for an image type
68 @param h: handler: ImageHandler subclass
69 """
70 cls.imageHandlerClasses[h.ostype] = h
72 addImageHandlerClass = classmethod(addImageHandlerClass)
74 def findImageHandlerClass(cls, image):
75 """Find the image handler class for an image config.
77 @param image config
78 @return ImageHandler subclass or None
79 """
80 ty = sxp.name(image)
81 if ty is None:
82 raise VmError('missing image type')
83 imageClass = cls.imageHandlerClasses.get(ty)
84 if imageClass is None:
85 raise VmError('unknown image type: ' + ty)
86 return imageClass
88 findImageHandlerClass = classmethod(findImageHandlerClass)
90 def create(cls, vm, image):
91 """Create an image handler for a vm.
93 @param vm vm
94 @param image image config
95 @return ImageHandler instance
96 """
97 imageClass = cls.findImageHandlerClass(image)
98 return imageClass(vm, image)
100 create = classmethod(create)
102 #======================================================================
103 # Instance vars and methods.
105 db = None
106 ostype = None
108 config = None
109 kernel = None
110 ramdisk = None
111 cmdline = None
112 flags = 0
114 __exports__ = [
115 DBVar('ostype', ty='str'),
116 DBVar('config', ty='sxpr'),
117 DBVar('kernel', ty='str'),
118 DBVar('ramdisk', ty='str'),
119 DBVar('cmdline', ty='str'),
120 DBVar('flags', ty='int'),
121 ]
123 def __init__(self, vm, config):
124 self.vm = vm
125 self.db = vm.db.addChild('/image')
126 self.config = config
128 def exportToDB(self, save=False, sync=False):
129 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
131 def importFromDB(self):
132 self.db.importFromDB(self, fields=self.__exports__)
134 def unlink(self, f):
135 if not f: return
136 try:
137 os.unlink(f)
138 except OSError, ex:
139 log.warning("error removing bootloader file '%s': %s", f, ex)
141 def initDomain(self, dom, memory, ssidref, cpu, cpu_weight):
142 """Initial domain create.
144 @return domain id
145 """
147 mem_kb = self.getDomainMemory(memory)
148 if not self.vm.restore:
149 dom = xc.domain_create(dom = dom or 0, ssidref = ssidref)
150 # if bootloader, unlink here. But should go after buildDomain() ?
151 if self.vm.bootloader:
152 self.unlink(self.kernel)
153 self.unlink(self.ramdisk)
154 if dom <= 0:
155 raise VmError('Creating domain failed: name=%s' % self.vm.name)
156 log.debug("initDomain: cpu=%d mem_kb=%d ssidref=%d dom=%d", cpu, mem_kb, ssidref, dom)
157 # xc.domain_setuuid(dom, uuid)
158 xc.domain_setcpuweight(dom, cpu_weight)
159 xc.domain_setmaxmem(dom, mem_kb)
161 try:
162 # Give the domain some memory below 4GB
163 lmem_kb = 4096
164 xc.domain_memory_increase_reservation(dom, min(lmem_kb,mem_kb), 0, 32)
165 if mem_kb > lmem_kb:
166 xc.domain_memory_increase_reservation(dom, mem_kb-lmem_kb, 0, 0)
167 except:
168 xc.domain_destroy(dom)
169 raise
171 if cpu != -1:
172 xc.domain_pincpu(dom, 0, 1<<int(cpu))
173 return dom
175 def createImage(self):
176 """Entry point to create domain memory image.
177 Override in subclass if needed.
178 """
179 self.configure()
180 self.createDomain()
182 def configure(self):
183 """Config actions common to all unix-like domains."""
184 self.kernel = sxp.child_value(self.config, "kernel")
185 self.cmdline = ""
186 ip = sxp.child_value(self.config, "ip", None)
187 if ip:
188 self.cmdline += " ip=" + ip
189 root = sxp.child_value(self.config, "root")
190 if root:
191 self.cmdline += " root=" + root
192 args = sxp.child_value(self.config, "args")
193 if args:
194 self.cmdline += " " + args
195 self.ramdisk = sxp.child_value(self.config, "ramdisk", '')
197 def createDomain(self):
198 """Build the domain boot image.
199 """
200 # Set params and call buildDomain().
201 self.flags = 0
202 if self.vm.netif_backend: self.flags |= SIF_NET_BE_DOMAIN
203 if self.vm.blkif_backend: self.flags |= SIF_BLK_BE_DOMAIN
204 if self.vm.tpmif_backend: self.flags |= SIF_TPM_BE_DOMAIN
206 if self.vm.recreate or self.vm.restore:
207 return
208 if not os.path.isfile(self.kernel):
209 raise VmError('Kernel image does not exist: %s' % self.kernel)
210 if self.ramdisk and not os.path.isfile(self.ramdisk):
211 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
212 if len(self.cmdline) >= 256:
213 log.warning('kernel cmdline too long, domain %d', self.vm.getDomain())
215 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
216 self.vm.getDomain(), self.vm.vcpus)
217 err = self.buildDomain()
218 if err != 0:
219 raise VmError('Building domain failed: ostype=%s dom=%d err=%d'
220 % (self.ostype, self.vm.getDomain(), err))
222 def getDomainMemory(self, mem_mb):
223 """Memory (in KB) the domain will need for mem_mb (in MB)."""
224 return mem_mb * 1024
226 def buildDomain(self):
227 """Build the domain. Define in subclass."""
228 raise NotImplementedError()
230 def createDeviceModel(self):
231 """Create device model for the domain (define in subclass if needed)."""
232 pass
234 def destroy(self):
235 """Extra cleanup on domain destroy (define in subclass if needed)."""
236 pass
238 addImageHandlerClass = ImageHandler.addImageHandlerClass
240 class LinuxImageHandler(ImageHandler):
242 ostype = "linux"
244 def buildDomain(self):
245 if self.vm.store_channel:
246 store_evtchn = self.vm.store_channel.port2
247 else:
248 store_evtchn = 0
249 if self.vm.console_channel:
250 console_evtchn = self.vm.console_channel.port2
251 else:
252 console_evtchn = 0
254 log.debug("dom = %d", self.vm.getDomain())
255 log.debug("image = %s", self.kernel)
256 log.debug("store_evtchn = %d", store_evtchn)
257 log.debug("console_evtchn = %d", console_evtchn)
258 log.debug("cmdline = %s", self.cmdline)
259 log.debug("ramdisk = %s", self.ramdisk)
260 log.debug("flags = %d", self.flags)
261 log.debug("vcpus = %d", self.vm.vcpus)
263 ret = xc.linux_build(dom = self.vm.getDomain(),
264 image = self.kernel,
265 store_evtchn = store_evtchn,
266 console_evtchn = console_evtchn,
267 cmdline = self.cmdline,
268 ramdisk = self.ramdisk,
269 flags = self.flags,
270 vcpus = self.vm.vcpus)
271 if isinstance(ret, dict):
272 self.vm.store_mfn = ret.get('store_mfn')
273 self.vm.console_mfn = ret.get('console_mfn')
274 return 0
275 return ret
277 class VmxImageHandler(ImageHandler):
279 __exports__ = ImageHandler.__exports__ + [
280 DBVar('memmap', ty='str'),
281 DBVar('memmap_value', ty='sxpr'),
282 # device channel?
283 ]
285 ostype = "vmx"
286 memmap = None
287 memmap_value = []
288 device_channel = None
289 pid = 0
290 def createImage(self):
291 """Create a VM for the VMX environment.
292 """
293 self.configure()
294 self.parseMemmap()
295 self.createDomain()
297 def buildDomain(self):
298 # Create an event channel
299 self.device_channel = channel.eventChannel(0, self.vm.getDomain())
300 log.info("VMX device model port: %d", self.device_channel.port2)
301 if self.vm.store_channel:
302 store_evtchn = self.vm.store_channel.port2
303 else:
304 store_evtchn = 0
305 ret = xc.vmx_build(dom = self.vm.getDomain(),
306 image = self.kernel,
307 control_evtchn = self.device_channel.port2,
308 store_evtchn = store_evtchn,
309 memsize = self.vm.memory,
310 memmap = self.memmap_value,
311 cmdline = self.cmdline,
312 ramdisk = self.ramdisk,
313 flags = self.flags,
314 vcpus = self.vm.vcpus)
315 if isinstance(ret, dict):
316 self.vm.store_mfn = ret.get('store_mfn')
317 return 0
318 return ret
320 def parseMemmap(self):
321 self.memmap = sxp.child_value(self.vm.config, "memmap")
322 if self.memmap is None:
323 return
324 memmap = sxp.parse(open(self.memmap))[0]
325 from xen.util.memmap import memmap_parse
326 self.memmap_value = memmap_parse(memmap)
328 # Return a list of cmd line args to the device models based on the
329 # xm config file
330 def parseDeviceModelArgs(self):
331 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb',
332 'localtime', 'serial', 'stdvga', 'isa' ]
333 ret = []
334 for a in dmargs:
335 v = sxp.child_value(self.vm.config, a)
337 # python doesn't allow '-' in variable names
338 if a == 'stdvga': a = 'std-vga'
340 # Handle booleans gracefully
341 if a in ['localtime', 'std-vga', 'isa']:
342 if v != None: v = int(v)
344 log.debug("args: %s, val: %s" % (a,v))
345 if v:
346 ret.append("-%s" % a)
347 ret.append("%s" % v)
349 # Handle disk/network related options
350 devices = sxp.children(self.vm.config, 'device')
351 for device in devices:
352 name = sxp.name(sxp.child0(device))
353 if name == 'vbd':
354 vbdinfo = sxp.child(device, 'vbd')
355 uname = sxp.child_value(vbdinfo, 'uname')
356 typedev = sxp.child_value(vbdinfo, 'dev')
357 (vbdtype, vbdparam) = string.split(uname, ':', 1)
358 if re.match('^ioemu:', typedev):
359 (emtype, vbddev) = string.split(typedev, ':', 1)
360 else:
361 emtype = 'vbd'
362 vbddev = typedev
363 if emtype != 'ioemu':
364 continue;
365 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
366 if vbddev not in vbddev_list:
367 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
368 ret.append("-%s" % vbddev)
369 ret.append("%s" % vbdparam)
370 if name == 'vif':
371 vifinfo = sxp.child(device, 'vif')
372 mac = sxp.child_value(vifinfo, 'mac')
373 ret.append("-macaddr")
374 ret.append("%s" % mac)
375 if name == 'vtpm':
376 vtpminfo = sxp.child(device, 'vtpm')
377 instance = sxp.child_value(vtpminfo, 'instance')
378 ret.append("-instance")
379 ret.append("%s" % instance)
381 # Handle graphics library related options
382 vnc = sxp.child_value(self.vm.config, 'vnc')
383 sdl = sxp.child_value(self.vm.config, 'sdl')
384 nographic = sxp.child_value(self.vm.config, 'nographic')
385 if nographic:
386 ret.append('-nographic')
387 return ret
389 if vnc and sdl:
390 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
391 elif vnc:
392 ret = ret + ['-vnc', '-k', 'en-us']
393 if vnc:
394 vncport = int(self.vm.getDomain()) + 5900
395 ret = ret + ['-vncport', '%d' % vncport]
396 return ret
398 def createDeviceModel(self):
399 device_model = sxp.child_value(self.vm.config, 'device_model')
400 if not device_model:
401 raise VmError("vmx: missing device model")
402 # Execute device model.
403 #todo: Error handling
404 # XXX RN: note that the order of args matter!
405 args = [device_model]
406 vnc = self.vncParams()
407 if len(vnc):
408 args = args + vnc
409 args = args + ([ "-d", "%d" % self.vm.getDomain(),
410 "-p", "%d" % self.device_channel.port1,
411 "-m", "%s" % self.vm.memory ])
412 args = args + self.parseDeviceModelArgs()
413 env = dict(os.environ)
414 env['DISPLAY'] = sxp.child_value(self.vm.config, 'display')
415 log.info("spawning device models: %s %s", device_model, args)
416 self.pid = os.spawnve(os.P_NOWAIT, device_model, args, env)
417 log.info("device model pid: %d", self.pid)
418 return self.pid
420 def vncParams(self):
421 # see if a vncviewer was specified
422 # XXX RN: bit of a hack. should unify this, maybe stick in config space
423 vncconnect=[]
424 image = self.config
425 args = sxp.child_value(image, "args")
426 if args:
427 arg_list = string.split(args)
428 for arg in arg_list:
429 al = string.split(arg, '=')
430 if al[0] == "VNC_VIEWER":
431 vncconnect=["-vncconnect", "%s" % al[1]]
432 break
433 return vncconnect
435 def destroy(self):
436 channel.eventChannelClose(self.device_channel)
437 import signal
438 if not self.pid:
439 self.pid = self.vm.device_model_pid
440 os.kill(self.pid, signal.SIGKILL)
441 (pid, status) = os.waitpid(self.pid, 0)
442 self.pid = 0
444 def getDomainMemory(self, mem_mb):
445 # for ioreq_t and xenstore
446 static_pages = 2
447 return (mem_mb * 1024) + self.getPageTableSize(mem_mb) + 4 * static_pages
449 def getPageTableSize(self, mem_mb):
450 """Return the size of memory needed for 1:1 page tables for physical
451 mode.
453 @param mem_mb: size in MB
454 @return size in KB
455 """
456 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
457 if os.uname()[4] == 'x86_64':
458 return (5 + ((mem_mb + 1) >> 1)) * 4
459 else:
460 return (1 + ((mem_mb + 3) >> 2)) * 4