ia64/xen-unstable

view tools/python/xen/xend/image.py @ 6911:a434b5449d59

Fix the control panel problem.
The new image handling structure broke the vmx guest loading.
Signed-off-by: Xiaofeng Ling <xiaofeng.ling@intel.com>
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Fri Sep 16 18:50:50 2005 +0000 (2005-09-16)
parents fd19e760932d
children a4cf3e17bb25
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
26 from xen.xend.xenstore.xstransact import xstransact
28 from xen.xend.server import channel
30 MAX_GUEST_CMDLINE = 1024
32 class ImageHandler:
33 """Abstract base class for image handlers.
35 initDomain() is called to initialise the domain memory and parse
36 the configuration.
38 createImage() is called to configure and build the domain from its
39 kernel image and ramdisk etc.
41 The method buildDomain() is used to build the domain, and must be
42 defined in a subclass. Usually this is the only method that needs
43 defining in a subclass.
45 The method createDeviceModel() is called to create the domain device
46 model if it needs one. The default is to do nothing.
48 The method destroy() is called when the domain is destroyed.
49 The default is to do nothing.
51 """
53 #======================================================================
54 # Class vars and methods.
56 """Table of image handler classes for virtual machine images.
57 Indexed by image type.
58 """
59 imageHandlerClasses = {}
61 def addImageHandlerClass(cls, h):
62 """Add a handler class for an image type
63 @param h: handler: ImageHandler subclass
64 """
65 cls.imageHandlerClasses[h.ostype] = h
67 addImageHandlerClass = classmethod(addImageHandlerClass)
69 def findImageHandlerClass(cls, image):
70 """Find the image handler class for an image config.
72 @param image config
73 @return ImageHandler subclass or None
74 """
75 ty = sxp.name(image)
76 if ty is None:
77 raise VmError('missing image type')
78 imageClass = cls.imageHandlerClasses.get(ty)
79 if imageClass is None:
80 raise VmError('unknown image type: ' + ty)
81 return imageClass
83 findImageHandlerClass = classmethod(findImageHandlerClass)
85 def create(cls, vm, image):
86 """Create an image handler for a vm.
88 @param vm vm
89 @param image image config
90 @return ImageHandler instance
91 """
92 imageClass = cls.findImageHandlerClass(image)
93 return imageClass(vm, image)
95 create = classmethod(create)
97 #======================================================================
98 # Instance vars and methods.
100 ostype = None
102 kernel = None
103 ramdisk = None
104 cmdline = None
106 flags = 0
108 def __init__(self, vm, config=None):
109 self.vm = vm
110 self.configure(config)
112 def configure(self, config):
113 """Config actions common to all unix-like domains."""
114 if not config:
115 self.kernel, self.cmdline, self.ramdisk = self.vm.gatherVm(
116 ("image/kernel"), ("image/cmdline"), ("image/ramdisk"))
117 return
119 self.kernel = sxp.child_value(config, "kernel")
120 self.cmdline = ""
121 ip = sxp.child_value(config, "ip", None)
122 if ip:
123 self.cmdline += " ip=" + ip
124 root = sxp.child_value(config, "root")
125 if root:
126 self.cmdline += " root=" + root
127 args = sxp.child_value(config, "args")
128 if args:
129 self.cmdline += " " + args
130 self.ramdisk = sxp.child_value(config, "ramdisk", '')
132 self.vm.storeVm(("image/ostype", self.ostype),
133 ("image/kernel", self.kernel),
134 ("image/cmdline", self.cmdline),
135 ("image/ramdisk", self.ramdisk))
137 def unlink(self, f):
138 if not f: return
139 try:
140 os.unlink(f)
141 except OSError, ex:
142 log.warning("error removing bootloader file '%s': %s", f, ex)
144 def initDomain(self, dom, memory, ssidref, cpu, cpu_weight):
145 """Initial domain create.
147 @return domain id
148 """
150 mem_kb = self.getDomainMemory(memory)
151 if not self.vm.restore:
152 dom = xc.domain_create(dom = dom or 0, ssidref = ssidref)
153 # if bootloader, unlink here. But should go after buildDomain() ?
154 if self.vm.bootloader:
155 self.unlink(self.kernel)
156 self.unlink(self.ramdisk)
157 if dom <= 0:
158 raise VmError('Creating domain failed: name=%s' % self.vm.name)
159 log.debug("initDomain: cpu=%d mem_kb=%d ssidref=%d dom=%d", cpu, mem_kb, ssidref, dom)
160 xc.domain_setcpuweight(dom, cpu_weight)
161 xc.domain_setmaxmem(dom, mem_kb)
163 try:
164 xc.domain_memory_increase_reservation(dom, mem_kb, 0, 0)
165 except:
166 xc.domain_destroy(dom)
167 raise
169 if cpu != -1:
170 xc.domain_pincpu(dom, 0, 1<<int(cpu))
171 return dom
173 def createImage(self):
174 """Entry point to create domain memory image.
175 Override in subclass if needed.
176 """
177 self.createDomain()
179 def createDomain(self):
180 """Build the domain boot image.
181 """
182 if self.vm.recreate or self.vm.restore:
183 return
185 # Set params and call buildDomain().
186 self.flags = self.vm.backend_flags
188 if not os.path.isfile(self.kernel):
189 raise VmError('Kernel image does not exist: %s' % self.kernel)
190 if self.ramdisk and not os.path.isfile(self.ramdisk):
191 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
192 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
193 log.warning('kernel cmdline too long, domain %d', self.vm.domid)
195 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
196 self.vm.domid, self.vm.vcpus)
197 err = self.buildDomain()
198 if err != 0:
199 raise VmError('Building domain failed: ostype=%s dom=%d err=%d'
200 % (self.ostype, self.vm.domid, err))
202 def getDomainMemory(self, mem_mb):
203 """Memory (in KB) the domain will need for mem_mb (in MB)."""
204 return mem_mb * 1024
206 def buildDomain(self):
207 """Build the domain. Define in subclass."""
208 raise NotImplementedError()
210 def createDeviceModel(self):
211 """Create device model for the domain (define in subclass if needed)."""
212 pass
214 def destroy(self):
215 """Extra cleanup on domain destroy (define in subclass if needed)."""
216 pass
218 def set_vminfo(self, d):
219 if d.has_key('store_mfn'):
220 self.vm.setStoreRef(d.get('store_mfn'))
221 if d.has_key('console_mfn'):
222 self.vm.setConsoleRef(d.get('console_mfn'))
224 addImageHandlerClass = ImageHandler.addImageHandlerClass
226 class LinuxImageHandler(ImageHandler):
228 ostype = "linux"
230 def buildDomain(self):
231 if self.vm.store_channel:
232 store_evtchn = self.vm.store_channel.port2
233 else:
234 store_evtchn = 0
235 if self.vm.console_channel:
236 console_evtchn = self.vm.console_channel.port2
237 else:
238 console_evtchn = 0
240 log.debug("dom = %d", self.vm.domid)
241 log.debug("image = %s", self.kernel)
242 log.debug("store_evtchn = %d", store_evtchn)
243 log.debug("console_evtchn = %d", console_evtchn)
244 log.debug("cmdline = %s", self.cmdline)
245 log.debug("ramdisk = %s", self.ramdisk)
246 log.debug("flags = %d", self.flags)
247 log.debug("vcpus = %d", self.vm.vcpus)
249 ret = xc.linux_build(dom = self.vm.domid,
250 image = self.kernel,
251 store_evtchn = store_evtchn,
252 console_evtchn = console_evtchn,
253 cmdline = self.cmdline,
254 ramdisk = self.ramdisk,
255 flags = self.flags,
256 vcpus = self.vm.vcpus)
257 if isinstance(ret, dict):
258 self.set_vminfo(ret)
259 return 0
260 return ret
262 class VmxImageHandler(ImageHandler):
264 ostype = "vmx"
266 memmap = None
267 memmap_value = []
268 device_channel = None
269 pid = 0
271 def configure(self, config):
272 ImageHandler.configure(self, config)
273 if not config:
274 self.memmap, dmargs, self.device_model, self.display = self.vm.gatherVm(
275 ("image/memmap"), ("image/dmargs"), ("image/device-model"),
276 ("image/display"))
277 self.dmargs = dmargs.split(' ')
278 return
280 self.memmap = sxp.child_value(config, 'memmap')
281 self.dmargs = self.parseDeviceModelArgs(config)
282 self.device_model = sxp.child_value(config, 'device_model')
283 if not self.device_model:
284 raise VmError("vmx: missing device model")
285 self.display = sxp.child_value(config, 'display')
287 self.vm.storeVm(("image/memmap", self.memmap),
288 ("image/dmargs", " ".join(self.dmargs)),
289 ("image/device-model", self.device_model),
290 ("image/display", self.display))
292 def createImage(self):
293 """Create a VM for the VMX environment.
294 """
295 self.parseMemmap()
296 self.createDomain()
298 def buildDomain(self):
299 # Create an event channel
300 self.device_channel = channel.eventChannel(0, self.vm.domid)
301 log.info("VMX device model port: %d", self.device_channel.port2)
302 if self.vm.store_channel:
303 store_evtchn = self.vm.store_channel.port2
304 else:
305 store_evtchn = 0
306 ret = xc.vmx_build(dom = self.vm.domid,
307 image = self.kernel,
308 control_evtchn = self.device_channel.port2,
309 store_evtchn = store_evtchn,
310 memsize = self.vm.memory,
311 memmap = self.memmap_value,
312 cmdline = self.cmdline,
313 ramdisk = self.ramdisk,
314 flags = self.flags,
315 vcpus = self.vm.vcpus)
316 if isinstance(ret, dict):
317 self.set_vminfo(ret)
318 return 0
319 return ret
321 def parseMemmap(self):
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, config):
331 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb',
332 'localtime', 'serial', 'stdvga', 'isa' ]
333 ret = []
334 for a in dmargs:
335 v = sxp.child_value(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(config, 'vnc')
383 sdl = sxp.child_value(config, 'sdl')
384 nographic = sxp.child_value(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.domid) + 5900
395 ret = ret + ['-vncport', '%d' % vncport]
396 return ret
398 def createDeviceModel(self):
399 if self.pid:
400 return
401 # Execute device model.
402 #todo: Error handling
403 # XXX RN: note that the order of args matter!
404 args = [self.device_model]
405 vnc = self.vncParams()
406 if len(vnc):
407 args = args + vnc
408 args = args + ([ "-d", "%d" % self.vm.domid,
409 "-p", "%d" % self.device_channel.port1,
410 "-m", "%s" % self.vm.memory ])
411 args = args + self.dmargs
412 env = dict(os.environ)
413 env['DISPLAY'] = self.display
414 log.info("spawning device models: %s %s", self.device_model, args)
415 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
416 log.info("device model pid: %d", self.pid)
418 def vncParams(self):
419 # see if a vncviewer was specified
420 # XXX RN: bit of a hack. should unify this, maybe stick in config space
421 vncconnect=[]
422 args = self.cmdline
423 if args:
424 arg_list = string.split(args)
425 for arg in arg_list:
426 al = string.split(arg, '=')
427 if al[0] == "VNC_VIEWER":
428 vncconnect=["-vncconnect", "%s" % al[1]]
429 break
430 return vncconnect
432 def destroy(self):
433 channel.eventChannelClose(self.device_channel)
434 import signal
435 if not self.pid:
436 return
437 os.kill(self.pid, signal.SIGKILL)
438 (pid, status) = os.waitpid(self.pid, 0)
439 self.pid = 0
441 def getDomainMemory(self, mem_mb):
442 # for ioreq_t and xenstore
443 static_pages = 2
444 return (mem_mb * 1024) + self.getPageTableSize(mem_mb) + 4 * static_pages
446 def getPageTableSize(self, mem_mb):
447 """Return the size of memory needed for 1:1 page tables for physical
448 mode.
450 @param mem_mb: size in MB
451 @return size in KB
452 """
453 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
454 if os.uname()[4] == 'x86_64':
455 return (5 + ((mem_mb + 1) >> 1)) * 4
456 elif os.uname()[4] == 'ia64':
457 # XEN/IA64 has p2m table allocated on demand, so only return
458 # guest firmware size here.
459 return 16 * 1024
460 else:
461 return (1 + ((mem_mb + 3) >> 2)) * 4