ia64/xen-unstable

view tools/python/xen/xend/image.py @ 7013:11cca45a9a79

this patch fixes the bug that when "cpu" is not set in config file,
control panel complains "Error creating domain - int argument required".
Signed-off-by: Xin Li <xin.b.li@intel.com>
author emellor@ewan
date Thu Sep 22 11:09:11 2005 +0100 (2005-09-22)
parents 55fc0ecc19c3
children 0f71667deb52
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
303 ret = xc.vmx_build(dom = self.vm.getDomain(),
304 image = self.kernel,
305 control_evtchn = self.device_channel.port2,
306 store_evtchn = store_evtchn,
307 memsize = self.vm.getMemoryTarget() / 1024,
308 memmap = self.memmap_value,
309 cmdline = self.cmdline,
310 ramdisk = self.ramdisk,
311 flags = self.flags,
312 vcpus = self.vm.getVCpuCount())
313 if isinstance(ret, dict):
314 self.set_vminfo(ret)
315 return 0
316 return ret
318 def parseMemmap(self):
319 if self.memmap is None:
320 return
321 memmap = sxp.parse(open(self.memmap))[0]
322 from xen.util.memmap import memmap_parse
323 self.memmap_value = memmap_parse(memmap)
325 # Return a list of cmd line args to the device models based on the
326 # xm config file
327 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
328 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb',
329 'localtime', 'serial', 'stdvga', 'isa', 'vcpus' ]
330 ret = []
331 for a in dmargs:
332 v = sxp.child_value(imageConfig, a)
334 # python doesn't allow '-' in variable names
335 if a == 'stdvga': a = 'std-vga'
337 # Handle booleans gracefully
338 if a in ['localtime', 'std-vga', 'isa']:
339 if v != None: v = int(v)
341 log.debug("args: %s, val: %s" % (a,v))
342 if v:
343 ret.append("-%s" % a)
344 ret.append("%s" % v)
346 # Handle disk/network related options
347 for (name, info) in deviceConfig:
348 if name == 'vbd':
349 uname = sxp.child_value(info, 'uname')
350 typedev = sxp.child_value(info, 'dev')
351 (_, vbdparam) = string.split(uname, ':', 1)
352 if re.match('^ioemu:', typedev):
353 (emtype, vbddev) = string.split(typedev, ':', 1)
354 else:
355 emtype = 'vbd'
356 vbddev = typedev
357 if emtype != 'ioemu':
358 continue;
359 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
360 if vbddev not in vbddev_list:
361 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
362 ret.append("-%s" % vbddev)
363 ret.append("%s" % vbdparam)
364 if name == 'vif':
365 mac = sxp.child_value(info, 'mac')
366 ret.append("-macaddr")
367 ret.append("%s" % mac)
368 if name == 'vtpm':
369 instance = sxp.child_value(info, 'instance')
370 ret.append("-instance")
371 ret.append("%s" % instance)
372 return ret
374 def configVNC(self, config):
375 # Handle graphics library related options
376 vnc = sxp.child_value(config, 'vnc')
377 sdl = sxp.child_value(config, 'sdl')
378 ret = []
379 nographic = sxp.child_value(config, 'nographic')
380 if nographic:
381 ret.append('-nographic')
382 return ret
384 if vnc and sdl:
385 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
386 elif vnc:
387 ret = ret + ['-vnc', '-k', 'en-us']
388 if vnc:
389 vncport = int(self.vm.getDomain()) + 5900
390 ret = ret + ['-vncport', '%d' % vncport]
391 return ret
393 def createDeviceModel(self):
394 if self.pid:
395 return
396 # Execute device model.
397 #todo: Error handling
398 # XXX RN: note that the order of args matter!
399 args = [self.device_model]
400 vnc = self.vncParams()
401 if len(vnc):
402 args = args + vnc
403 args = args + ([ "-d", "%d" % self.vm.getDomain(),
404 "-p", "%d" % self.device_channel.port1,
405 "-m", "%s" % self.vm.getMemoryTarget() / 1024 ])
406 args = args + self.dmargs
407 env = dict(os.environ)
408 env['DISPLAY'] = self.display
409 log.info("spawning device models: %s %s", self.device_model, args)
410 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
411 log.info("device model pid: %d", self.pid)
413 def vncParams(self):
414 # see if a vncviewer was specified
415 # XXX RN: bit of a hack. should unify this, maybe stick in config space
416 vncconnect=[]
417 args = self.cmdline
418 if args:
419 arg_list = string.split(args)
420 for arg in arg_list:
421 al = string.split(arg, '=')
422 if al[0] == "VNC_VIEWER":
423 vncconnect=["-vncconnect", "%s" % al[1]]
424 break
425 return vncconnect
427 def destroy(self):
428 channel.eventChannelClose(self.device_channel)
429 import signal
430 if not self.pid:
431 return
432 os.kill(self.pid, signal.SIGKILL)
433 os.waitpid(self.pid, 0)
434 self.pid = 0
436 def getDomainMemory(self, mem):
437 """@see ImageHandler.getDomainMemory"""
438 # for ioreq_t and xenstore
439 static_pages = 2
440 return mem + self.getPageTableSize(mem * 1024) + 4 * static_pages
442 def getPageTableSize(self, mem_mb):
443 """Return the size of memory needed for 1:1 page tables for physical
444 mode.
446 @param mem_mb: size in MB
447 @return size in KB
448 """
449 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
450 if os.uname()[4] == 'x86_64':
451 return (5 + ((mem_mb + 1) >> 1)) * 4
452 elif os.uname()[4] == 'ia64':
453 # XEN/IA64 has p2m table allocated on demand, so only return
454 # guest firmware size here.
455 return 16 * 1024
456 else:
457 return (1 + ((mem_mb + 3) >> 2)) * 4