ia64/xen-unstable

view tools/python/xen/xend/image.py @ 6979:f8e7af29daa1

merge?
author cl349@firebug.cl.cam.ac.uk
date Tue Sep 20 09:43:46 2005 +0000 (2005-09-20)
parents a6b72464a042 3133e64d0462
children 21e7935b2025 9647be59212d
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
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
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, image):
90 """Create an image handler for a vm.
92 @param vm vm
93 @param image image config
94 @return ImageHandler instance
95 """
96 imageClass = cls.findImageHandlerClass(image)
97 return imageClass(vm, image)
99 create = classmethod(create)
101 #======================================================================
102 # Instance vars and methods.
104 ostype = None
106 kernel = None
107 ramdisk = None
108 cmdline = None
110 flags = 0
112 def __init__(self, vm, config=None):
113 self.vm = vm
114 self.configure(config)
116 def configure(self, config):
117 """Config actions common to all unix-like domains."""
118 if not config:
119 self.kernel, self.cmdline, self.ramdisk = self.vm.gatherVm(
120 ("image/kernel"), ("image/cmdline"), ("image/ramdisk"))
121 return
123 self.kernel = sxp.child_value(config, "kernel")
124 self.cmdline = ""
125 ip = sxp.child_value(config, "ip", None)
126 if ip:
127 self.cmdline += " ip=" + ip
128 root = sxp.child_value(config, "root")
129 if root:
130 self.cmdline += " root=" + root
131 args = sxp.child_value(config, "args")
132 if args:
133 self.cmdline += " " + args
134 self.ramdisk = sxp.child_value(config, "ramdisk", '')
136 self.vm.storeVm(("image/ostype", self.ostype),
137 ("image/kernel", self.kernel),
138 ("image/cmdline", self.cmdline),
139 ("image/ramdisk", self.ramdisk))
141 def unlink(self, f):
142 if not f: return
143 try:
144 os.unlink(f)
145 except OSError, ex:
146 log.warning("error removing bootloader file '%s': %s", f, ex)
148 def initDomain(self, dom, memory, ssidref, cpu, cpu_weight):
149 """Initial domain create.
151 @return domain id
152 """
154 mem_kb = self.getDomainMemory(memory)
155 if not self.vm.restore:
156 dom = xc.domain_create(dom = dom or 0, ssidref = ssidref)
157 # if bootloader, unlink here. But should go after buildDomain() ?
158 if self.vm.bootloader:
159 self.unlink(self.kernel)
160 self.unlink(self.ramdisk)
161 if dom <= 0:
162 raise VmError('Creating domain failed: name=%s' %
163 self.vm.getName())
164 log.debug("initDomain: cpu=%d mem_kb=%d ssidref=%d dom=%d", cpu, mem_kb, ssidref, dom)
165 xc.domain_setcpuweight(dom, cpu_weight)
166 xc.domain_setmaxmem(dom, mem_kb)
168 try:
169 xc.domain_memory_increase_reservation(dom, mem_kb, 0, 0)
170 except:
171 xc.domain_destroy(dom)
172 raise
174 if cpu != -1:
175 xc.domain_pincpu(dom, 0, 1<<int(cpu))
176 return dom
178 def createImage(self):
179 """Entry point to create domain memory image.
180 Override in subclass if needed.
181 """
182 self.createDomain()
184 def createDomain(self):
185 """Build the domain boot image.
186 """
187 if self.vm.recreate or self.vm.restore:
188 return
190 # Set params and call buildDomain().
191 self.flags = self.vm.getBackendFlags()
193 if not os.path.isfile(self.kernel):
194 raise VmError('Kernel image does not exist: %s' % self.kernel)
195 if self.ramdisk and not os.path.isfile(self.ramdisk):
196 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
197 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
198 log.warning('kernel cmdline too long, domain %d',
199 self.vm.getDomain())
201 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
202 self.vm.getDomain(), self.vm.getVCpuCount())
203 err = self.buildDomain()
204 if err != 0:
205 raise VmError('Building domain failed: ostype=%s dom=%d err=%d'
206 % (self.ostype, self.vm.getDomain(), err))
208 def getDomainMemory(self, mem_mb):
209 """Memory (in KB) the domain will need for mem_mb (in MB)."""
210 return mem_mb * 1024
212 def buildDomain(self):
213 """Build the domain. Define in subclass."""
214 raise NotImplementedError()
216 def createDeviceModel(self):
217 """Create device model for the domain (define in subclass if needed)."""
218 pass
220 def destroy(self):
221 """Extra cleanup on domain destroy (define in subclass if needed)."""
222 pass
224 def set_vminfo(self, d):
225 if d.has_key('store_mfn'):
226 self.vm.setStoreRef(d.get('store_mfn'))
227 if d.has_key('console_mfn'):
228 self.vm.setConsoleRef(d.get('console_mfn'))
230 addImageHandlerClass = ImageHandler.addImageHandlerClass
232 class LinuxImageHandler(ImageHandler):
234 ostype = "linux"
236 def buildDomain(self):
237 if self.vm.store_channel:
238 store_evtchn = self.vm.store_channel.port2
239 else:
240 store_evtchn = 0
241 if self.vm.console_channel:
242 console_evtchn = self.vm.console_channel.port2
243 else:
244 console_evtchn = 0
246 log.debug("dom = %d", self.vm.getDomain())
247 log.debug("image = %s", self.kernel)
248 log.debug("store_evtchn = %d", store_evtchn)
249 log.debug("console_evtchn = %d", console_evtchn)
250 log.debug("cmdline = %s", self.cmdline)
251 log.debug("ramdisk = %s", self.ramdisk)
252 log.debug("flags = %d", self.flags)
253 log.debug("vcpus = %d", self.vm.getVCpuCount())
255 ret = xc.linux_build(dom = self.vm.getDomain(),
256 image = self.kernel,
257 store_evtchn = store_evtchn,
258 console_evtchn = console_evtchn,
259 cmdline = self.cmdline,
260 ramdisk = self.ramdisk,
261 flags = self.flags,
262 vcpus = self.vm.getVCpuCount())
263 if isinstance(ret, dict):
264 self.set_vminfo(ret)
265 return 0
266 return ret
268 class VmxImageHandler(ImageHandler):
270 ostype = "vmx"
272 memmap = None
273 memmap_value = []
274 device_channel = None
275 pid = 0
277 def configure(self, config):
278 ImageHandler.configure(self, config)
279 if not config:
280 self.memmap, dmargs, self.device_model, self.display = self.vm.gatherVm(
281 ("image/memmap"), ("image/dmargs"), ("image/device-model"),
282 ("image/display"))
283 self.dmargs = dmargs.split(' ')
284 return
286 self.memmap = sxp.child_value(config, 'memmap')
287 self.dmargs = self.parseDeviceModelArgs(config)
288 self.device_model = sxp.child_value(config, 'device_model')
289 if not self.device_model:
290 raise VmError("vmx: missing device model")
291 self.display = sxp.child_value(config, 'display')
293 self.vm.storeVm(("image/memmap", self.memmap),
294 ("image/dmargs", " ".join(self.dmargs)),
295 ("image/device-model", self.device_model),
296 ("image/display", self.display))
298 def createImage(self):
299 """Create a VM for the VMX environment.
300 """
301 self.parseMemmap()
302 self.createDomain()
303 self.dmargs += self.configVNC(sxp.child_value(self.vm.config, 'image'))
305 def buildDomain(self):
306 # Create an event channel
307 self.device_channel = channel.eventChannel(0, self.vm.getDomain())
308 log.info("VMX device model port: %d", self.device_channel.port2)
309 if self.vm.store_channel:
310 store_evtchn = self.vm.store_channel.port2
311 else:
312 store_evtchn = 0
313 ret = xc.vmx_build(dom = self.vm.getDomain(),
314 image = self.kernel,
315 control_evtchn = self.device_channel.port2,
316 store_evtchn = store_evtchn,
317 memsize = self.vm.getMemoryTarget(),
318 memmap = self.memmap_value,
319 cmdline = self.cmdline,
320 ramdisk = self.ramdisk,
321 flags = self.flags,
322 vcpus = self.vm.getVCpuCount())
323 if isinstance(ret, dict):
324 self.set_vminfo(ret)
325 return 0
326 return ret
328 def parseMemmap(self):
329 if self.memmap is None:
330 return
331 memmap = sxp.parse(open(self.memmap))[0]
332 from xen.util.memmap import memmap_parse
333 self.memmap_value = memmap_parse(memmap)
335 # Return a list of cmd line args to the device models based on the
336 # xm config file
337 def parseDeviceModelArgs(self, config):
338 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb',
339 'localtime', 'serial', 'stdvga', 'isa' ]
340 ret = []
341 for a in dmargs:
342 v = sxp.child_value(config, a)
344 # python doesn't allow '-' in variable names
345 if a == 'stdvga': a = 'std-vga'
347 # Handle booleans gracefully
348 if a in ['localtime', 'std-vga', 'isa']:
349 if v != None: v = int(v)
351 log.debug("args: %s, val: %s" % (a,v))
352 if v:
353 ret.append("-%s" % a)
354 ret.append("%s" % v)
356 # Handle disk/network related options
357 devices = sxp.children(self.vm.config, 'device')
358 for device in devices:
359 name = sxp.name(sxp.child0(device))
360 if name == 'vbd':
361 vbdinfo = sxp.child(device, 'vbd')
362 uname = sxp.child_value(vbdinfo, 'uname')
363 typedev = sxp.child_value(vbdinfo, 'dev')
364 (vbdtype, vbdparam) = string.split(uname, ':', 1)
365 if re.match('^ioemu:', typedev):
366 (emtype, vbddev) = string.split(typedev, ':', 1)
367 else:
368 emtype = 'vbd'
369 vbddev = typedev
370 if emtype != 'ioemu':
371 continue;
372 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
373 if vbddev not in vbddev_list:
374 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
375 ret.append("-%s" % vbddev)
376 ret.append("%s" % vbdparam)
377 if name == 'vif':
378 vifinfo = sxp.child(device, 'vif')
379 mac = sxp.child_value(vifinfo, 'mac')
380 ret.append("-macaddr")
381 ret.append("%s" % mac)
382 if name == 'vtpm':
383 vtpminfo = sxp.child(device, 'vtpm')
384 instance = sxp.child_value(vtpminfo, 'instance')
385 ret.append("-instance")
386 ret.append("%s" % instance)
387 return ret
389 def configVNC(self, config):
390 # Handle graphics library related options
391 vnc = sxp.child_value(config, 'vnc')
392 sdl = sxp.child_value(config, 'sdl')
393 ret = []
394 nographic = sxp.child_value(config, 'nographic')
395 if nographic:
396 ret.append('-nographic')
397 return ret
399 if vnc and sdl:
400 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
401 elif vnc:
402 ret = ret + ['-vnc', '-k', 'en-us']
403 if vnc:
404 vncport = int(self.vm.getDomain()) + 5900
405 ret = ret + ['-vncport', '%d' % vncport]
406 return ret
408 def createDeviceModel(self):
409 if self.pid:
410 return
411 # Execute device model.
412 #todo: Error handling
413 # XXX RN: note that the order of args matter!
414 args = [self.device_model]
415 vnc = self.vncParams()
416 if len(vnc):
417 args = args + vnc
418 args = args + ([ "-d", "%d" % self.vm.getDomain(),
419 "-p", "%d" % self.device_channel.port1,
420 "-m", "%s" % self.vm.getMemoryTarget() ])
421 args = args + self.dmargs
422 env = dict(os.environ)
423 env['DISPLAY'] = self.display
424 log.info("spawning device models: %s %s", self.device_model, args)
425 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
426 log.info("device model pid: %d", self.pid)
428 def vncParams(self):
429 # see if a vncviewer was specified
430 # XXX RN: bit of a hack. should unify this, maybe stick in config space
431 vncconnect=[]
432 args = self.cmdline
433 if args:
434 arg_list = string.split(args)
435 for arg in arg_list:
436 al = string.split(arg, '=')
437 if al[0] == "VNC_VIEWER":
438 vncconnect=["-vncconnect", "%s" % al[1]]
439 break
440 return vncconnect
442 def destroy(self):
443 channel.eventChannelClose(self.device_channel)
444 import signal
445 if not self.pid:
446 return
447 os.kill(self.pid, signal.SIGKILL)
448 (pid, status) = os.waitpid(self.pid, 0)
449 self.pid = 0
451 def getDomainMemory(self, mem_mb):
452 # for ioreq_t and xenstore
453 static_pages = 2
454 return (mem_mb * 1024) + self.getPageTableSize(mem_mb) + 4 * static_pages
456 def getPageTableSize(self, mem_mb):
457 """Return the size of memory needed for 1:1 page tables for physical
458 mode.
460 @param mem_mb: size in MB
461 @return size in KB
462 """
463 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
464 if os.uname()[4] == 'x86_64':
465 return (5 + ((mem_mb + 1) >> 1)) * 4
466 elif os.uname()[4] == 'ia64':
467 # XEN/IA64 has p2m table allocated on demand, so only return
468 # guest firmware size here.
469 return 16 * 1024
470 else:
471 return (1 + ((mem_mb + 3) >> 2)) * 4