ia64/xen-unstable

view tools/python/xen/xend/image.py @ 6708:aa0990ef260f

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