ia64/xen-unstable

view tools/python/xen/xend/image.py @ 6689:7d0fb56b4a91

merge?
author cl349@firebug.cl.cam.ac.uk
date Wed Sep 07 19:01:31 2005 +0000 (2005-09-07)
parents 549f4256ab3c dd668f7527cb
children 12ff9c954ace 2704a88c3295
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 xc.domain_memory_increase_reservation(dom, mem_kb)
163 except:
164 xc.domain_destroy(dom)
165 raise
167 if cpu != -1:
168 xc.domain_pincpu(dom, 0, 1<<int(cpu))
169 return dom
171 def createImage(self):
172 """Entry point to create domain memory image.
173 Override in subclass if needed.
174 """
175 self.configure()
176 self.createDomain()
178 def configure(self):
179 """Config actions common to all unix-like domains."""
180 self.kernel = sxp.child_value(self.config, "kernel")
181 self.cmdline = ""
182 ip = sxp.child_value(self.config, "ip", None)
183 if ip:
184 self.cmdline += " ip=" + ip
185 root = sxp.child_value(self.config, "root")
186 if root:
187 self.cmdline += " root=" + root
188 args = sxp.child_value(self.config, "args")
189 if args:
190 self.cmdline += " " + args
191 self.ramdisk = sxp.child_value(self.config, "ramdisk", '')
193 def createDomain(self):
194 """Build the domain boot image.
195 """
196 # Set params and call buildDomain().
197 self.flags = 0
198 if self.vm.netif_backend: self.flags |= SIF_NET_BE_DOMAIN
199 if self.vm.blkif_backend: self.flags |= SIF_BLK_BE_DOMAIN
200 if self.vm.tpmif_backend: self.flags |= SIF_TPM_BE_DOMAIN
202 if self.vm.recreate or self.vm.restore:
203 return
204 if not os.path.isfile(self.kernel):
205 raise VmError('Kernel image does not exist: %s' % self.kernel)
206 if self.ramdisk and not os.path.isfile(self.ramdisk):
207 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
208 if len(self.cmdline) >= 256:
209 log.warning('kernel cmdline too long, domain %d', self.vm.getDomain())
211 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
212 self.vm.getDomain(), self.vm.vcpus)
213 err = self.buildDomain()
214 if err != 0:
215 raise VmError('Building domain failed: ostype=%s dom=%d err=%d'
216 % (self.ostype, self.vm.getDomain(), err))
218 def getDomainMemory(self, mem_mb):
219 """Memory (in KB) the domain will need for mem_mb (in MB)."""
220 return mem_mb * 1024
222 def buildDomain(self):
223 """Build the domain. Define in subclass."""
224 raise NotImplementedError()
226 def createDeviceModel(self):
227 """Create device model for the domain (define in subclass if needed)."""
228 pass
230 def destroy(self):
231 """Extra cleanup on domain destroy (define in subclass if needed)."""
232 pass
234 addImageHandlerClass = ImageHandler.addImageHandlerClass
236 class LinuxImageHandler(ImageHandler):
238 ostype = "linux"
240 def buildDomain(self):
241 if self.vm.store_channel:
242 store_evtchn = self.vm.store_channel.port2
243 else:
244 store_evtchn = 0
245 if self.vm.console_channel:
246 console_evtchn = self.vm.console_channel.port2
247 else:
248 console_evtchn = 0
250 log.debug("dom = %d", self.vm.getDomain())
251 log.debug("image = %s", self.kernel)
252 log.debug("store_evtchn = %d", store_evtchn)
253 log.debug("console_evtchn = %d", console_evtchn)
254 log.debug("cmdline = %s", self.cmdline)
255 log.debug("ramdisk = %s", self.ramdisk)
256 log.debug("flags = %d", self.flags)
257 log.debug("vcpus = %d", self.vm.vcpus)
259 ret = xc.linux_build(dom = self.vm.getDomain(),
260 image = self.kernel,
261 store_evtchn = store_evtchn,
262 console_evtchn = console_evtchn,
263 cmdline = self.cmdline,
264 ramdisk = self.ramdisk,
265 flags = self.flags,
266 vcpus = self.vm.vcpus)
267 if isinstance(ret, dict):
268 self.vm.store_mfn = ret.get('store_mfn')
269 self.vm.console_mfn = ret.get('console_mfn')
270 return 0
271 return ret
273 class VmxImageHandler(ImageHandler):
275 __exports__ = ImageHandler.__exports__ + [
276 DBVar('memmap', ty='str'),
277 DBVar('memmap_value', ty='sxpr'),
278 # device channel?
279 ]
281 ostype = "vmx"
282 memmap = None
283 memmap_value = []
284 device_channel = None
285 pid = 0
286 def createImage(self):
287 """Create a VM for the VMX environment.
288 """
289 self.configure()
290 self.parseMemmap()
291 self.createDomain()
293 def buildDomain(self):
294 # Create an event channel
295 self.device_channel = channel.eventChannel(0, self.vm.getDomain())
296 log.info("VMX device model port: %d", self.device_channel.port2)
297 if self.vm.store_channel:
298 store_evtchn = self.vm.store_channel.port2
299 else:
300 store_evtchn = 0
301 ret = xc.vmx_build(dom = self.vm.getDomain(),
302 image = self.kernel,
303 control_evtchn = self.device_channel.port2,
304 store_evtchn = store_evtchn,
305 memsize = self.vm.memory,
306 memmap = self.memmap_value,
307 cmdline = self.cmdline,
308 ramdisk = self.ramdisk,
309 flags = self.flags,
310 vcpus = self.vm.vcpus)
311 if isinstance(ret, dict):
312 self.vm.store_mfn = ret.get('store_mfn')
313 return 0
314 return ret
316 def parseMemmap(self):
317 self.memmap = sxp.child_value(self.vm.config, "memmap")
318 if self.memmap is None:
319 return
320 memmap = sxp.parse(open(self.memmap))[0]
321 from xen.util.memmap import memmap_parse
322 self.memmap_value = memmap_parse(memmap)
324 # Return a list of cmd line args to the device models based on the
325 # xm config file
326 def parseDeviceModelArgs(self):
327 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb',
328 'localtime', 'serial', 'stdvga', 'isa' ]
329 ret = []
330 for a in dmargs:
331 v = sxp.child_value(self.vm.config, a)
333 # python doesn't allow '-' in variable names
334 if a == 'stdvga': a = 'std-vga'
336 # Handle booleans gracefully
337 if a in ['localtime', 'std-vga', 'isa']:
338 if v != None: v = int(v)
340 log.debug("args: %s, val: %s" % (a,v))
341 if v:
342 ret.append("-%s" % a)
343 ret.append("%s" % v)
345 # Handle disk/network related options
346 devices = sxp.children(self.vm.config, 'device')
347 for device in devices:
348 name = sxp.name(sxp.child0(device))
349 if name == 'vbd':
350 vbdinfo = sxp.child(device, 'vbd')
351 uname = sxp.child_value(vbdinfo, 'uname')
352 typedev = sxp.child_value(vbdinfo, 'dev')
353 (vbdtype, vbdparam) = string.split(uname, ':', 1)
354 if re.match('^ioemu:', typedev):
355 (emtype, vbddev) = string.split(typedev, ':', 1)
356 else:
357 emtype = 'vbd'
358 vbddev = typedev
359 if emtype != 'ioemu':
360 continue;
361 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
362 if vbddev not in vbddev_list:
363 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
364 ret.append("-%s" % vbddev)
365 ret.append("%s" % vbdparam)
366 if name == 'vif':
367 vifinfo = sxp.child(device, 'vif')
368 mac = sxp.child_value(vifinfo, 'mac')
369 ret.append("-macaddr")
370 ret.append("%s" % mac)
371 if name == 'vtpm':
372 vtpminfo = sxp.child(device, 'vtpm')
373 instance = sxp.child_value(vtpminfo, 'instance')
374 ret.append("-instance")
375 ret.append("%s" % instance)
377 # Handle graphics library related options
378 vnc = sxp.child_value(self.vm.config, 'vnc')
379 sdl = sxp.child_value(self.vm.config, 'sdl')
380 nographic = sxp.child_value(self.vm.config, 'nographic')
381 if nographic:
382 ret.append('-nographic')
383 return ret
385 if vnc and sdl:
386 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
387 elif vnc:
388 ret = ret + ['-vnc', '-k', 'en-us']
389 if vnc:
390 vncport = int(self.vm.getDomain()) + 5900
391 ret = ret + ['-vncport', '%d' % vncport]
392 return ret
394 def createDeviceModel(self):
395 device_model = sxp.child_value(self.vm.config, 'device_model')
396 if not device_model:
397 raise VmError("vmx: missing device model")
398 # Execute device model.
399 #todo: Error handling
400 # XXX RN: note that the order of args matter!
401 args = [device_model]
402 vnc = self.vncParams()
403 if len(vnc):
404 args = args + vnc
405 args = args + ([ "-d", "%d" % self.vm.getDomain(),
406 "-p", "%d" % self.device_channel.port1,
407 "-m", "%s" % self.vm.memory ])
408 args = args + self.parseDeviceModelArgs()
409 env = dict(os.environ)
410 env['DISPLAY'] = sxp.child_value(self.vm.config, 'display')
411 log.info("spawning device models: %s %s", device_model, args)
412 self.pid = os.spawnve(os.P_NOWAIT, device_model, args, env)
413 log.info("device model pid: %d", self.pid)
414 return self.pid
416 def vncParams(self):
417 # see if a vncviewer was specified
418 # XXX RN: bit of a hack. should unify this, maybe stick in config space
419 vncconnect=[]
420 image = self.config
421 args = sxp.child_value(image, "args")
422 if args:
423 arg_list = string.split(args)
424 for arg in arg_list:
425 al = string.split(arg, '=')
426 if al[0] == "VNC_VIEWER":
427 vncconnect=["-vncconnect", "%s" % al[1]]
428 break
429 return vncconnect
431 def destroy(self):
432 channel.eventChannelClose(self.device_channel)
433 import signal
434 if not self.pid:
435 self.pid = self.vm.device_model_pid
436 os.kill(self.pid, signal.SIGKILL)
437 (pid, status) = os.waitpid(self.pid, 0)
438 self.pid = 0
440 def getDomainMemory(self, mem_mb):
441 # for ioreq_t and xenstore
442 static_pages = 2
443 return (mem_mb * 1024) + self.getPageTableSize(mem_mb) + 4 * static_pages
445 def getPageTableSize(self, mem_mb):
446 """Return the size of memory needed for 1:1 page tables for physical
447 mode.
449 @param mem_mb: size in MB
450 @return size in KB
451 """
452 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
453 if os.uname()[4] == 'x86_64':
454 return (5 + ((mem_mb + 1) >> 1)) * 4
455 else:
456 return (1 + ((mem_mb + 3) >> 2)) * 4