ia64/xen-unstable

view tools/python/xen/xend/image.py @ 6552:a9873d384da4

Merge.
author adsharma@los-vmm.sc.intel.com
date Thu Aug 25 12:24:48 2005 -0700 (2005-08-25)
parents 112d44270733 fa0754a9f64f
children dfaf788ab18c
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 class ImageHandler:
36 """Abstract base class for image handlers.
38 initDomain() is called to initialise the domain memory.
40 createImage() is called to configure and build the domain from its
41 kernel image and ramdisk etc.
43 The method buildDomain() is used to build the domain, and must be
44 defined in a subclass. Usually this is the only method that needs
45 defining in a subclass.
47 The method createDeviceModel() is called to create the domain device
48 model if it needs one. The default is to do nothing.
50 The method destroy() is called when the domain is destroyed.
51 The default is to do nothing.
53 """
55 #======================================================================
56 # Class vars and methods.
58 """Table of image handler classes for virtual machine images.
59 Indexed by image type.
60 """
61 imageHandlerClasses = {}
63 def addImageHandlerClass(cls, h):
64 """Add a handler class for an image type
65 @param h: handler: ImageHandler subclass
66 """
67 cls.imageHandlerClasses[h.ostype] = h
69 addImageHandlerClass = classmethod(addImageHandlerClass)
71 def findImageHandlerClass(cls, image):
72 """Find the image handler class for an image config.
74 @param image config
75 @return ImageHandler subclass or None
76 """
77 ty = sxp.name(image)
78 if ty is None:
79 raise VmError('missing image type')
80 imageClass = cls.imageHandlerClasses.get(ty)
81 if imageClass is None:
82 raise VmError('unknown image type: ' + ty)
83 return imageClass
85 findImageHandlerClass = classmethod(findImageHandlerClass)
87 def create(cls, vm, image):
88 """Create an image handler for a vm.
90 @param vm vm
91 @param image image config
92 @return ImageHandler instance
93 """
94 imageClass = cls.findImageHandlerClass(image)
95 return imageClass(vm, image)
97 create = classmethod(create)
99 #======================================================================
100 # Instance vars and methods.
102 db = None
103 ostype = None
105 config = None
106 kernel = None
107 ramdisk = None
108 cmdline = None
109 flags = 0
111 __exports__ = [
112 DBVar('ostype', ty='str'),
113 DBVar('config', ty='sxpr'),
114 DBVar('kernel', ty='str'),
115 DBVar('ramdisk', ty='str'),
116 DBVar('cmdline', ty='str'),
117 DBVar('flags', ty='int'),
118 ]
120 def __init__(self, vm, config):
121 self.vm = vm
122 self.db = vm.db.addChild('/image')
123 self.config = config
125 def exportToDB(self, save=False, sync=False):
126 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
128 def importFromDB(self):
129 self.db.importFromDB(self, fields=self.__exports__)
131 def unlink(self, f):
132 if not f: return
133 try:
134 os.unlink(f)
135 except OSError, ex:
136 log.warning("error removing bootloader file '%s': %s", f, ex)
138 def initDomain(self, dom, memory, ssidref, cpu, cpu_weight):
139 """Initial domain create.
141 @return domain id
142 """
144 mem_kb = self.getDomainMemory(memory)
145 if not self.vm.restore:
146 dom = xc.domain_create(dom = dom or 0, ssidref = ssidref)
147 # if bootloader, unlink here. But should go after buildDomain() ?
148 if self.vm.bootloader:
149 self.unlink(self.kernel)
150 self.unlink(self.ramdisk)
151 if dom <= 0:
152 raise VmError('Creating domain failed: name=%s' % self.vm.name)
153 log.debug("initDomain: cpu=%d mem_kb=%d ssidref=%d dom=%d", cpu, mem_kb, ssidref, dom)
154 # xc.domain_setuuid(dom, uuid)
155 xc.domain_setcpuweight(dom, cpu_weight)
156 xc.domain_setmaxmem(dom, mem_kb)
158 try:
159 xc.domain_memory_increase_reservation(dom, mem_kb)
160 except:
161 xc.domain_destroy(dom)
162 raise
164 if cpu != -1:
165 xc.domain_pincpu(dom, 0, 1<<int(cpu))
166 return dom
168 def createImage(self):
169 """Entry point to create domain memory image.
170 Override in subclass if needed.
171 """
172 self.configure()
173 self.createDomain()
175 def configure(self):
176 """Config actions common to all unix-like domains."""
177 self.kernel = sxp.child_value(self.config, "kernel")
178 self.cmdline = ""
179 ip = sxp.child_value(self.config, "ip", None)
180 if ip:
181 self.cmdline += " ip=" + ip
182 root = sxp.child_value(self.config, "root")
183 if root:
184 self.cmdline += " root=" + root
185 args = sxp.child_value(self.config, "args")
186 if args:
187 self.cmdline += " " + args
188 self.ramdisk = sxp.child_value(self.config, "ramdisk", '')
190 def createDomain(self):
191 """Build the domain boot image.
192 """
193 # Set params and call buildDomain().
194 self.flags = 0
195 if self.vm.netif_backend: self.flags |= SIF_NET_BE_DOMAIN
196 if self.vm.blkif_backend: self.flags |= SIF_BLK_BE_DOMAIN
198 if self.vm.recreate or self.vm.restore:
199 return
200 if not os.path.isfile(self.kernel):
201 raise VmError('Kernel image does not exist: %s' % self.kernel)
202 if self.ramdisk and not os.path.isfile(self.ramdisk):
203 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
204 if len(self.cmdline) >= 256:
205 log.warning('kernel cmdline too long, domain %d', self.vm.getDomain())
207 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
208 self.vm.getDomain(), self.vm.vcpus)
209 err = self.buildDomain()
210 if err != 0:
211 raise VmError('Building domain failed: ostype=%s dom=%d err=%d'
212 % (self.ostype, self.vm.getDomain(), err))
214 def getDomainMemory(self, mem_mb):
215 """Memory (in KB) the domain will need for mem_mb (in MB)."""
216 return mem_mb * 1024
218 def buildDomain(self):
219 """Build the domain. Define in subclass."""
220 raise NotImplementedError()
222 def createDeviceModel(self):
223 """Create device model for the domain (define in subclass if needed)."""
224 pass
226 def destroy(self):
227 """Extra cleanup on domain destroy (define in subclass if needed)."""
228 pass
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 ret = xc.linux_build(dom = self.vm.getDomain(),
242 image = self.kernel,
243 control_evtchn = self.vm.channel.getRemotePort(),
244 store_evtchn = store_evtchn,
245 cmdline = self.cmdline,
246 ramdisk = self.ramdisk,
247 flags = self.flags,
248 vcpus = self.vm.vcpus)
249 if isinstance(ret, dict):
250 self.vm.store_mfn = ret.get('store_mfn')
251 return 0
252 return ret
254 class VmxImageHandler(ImageHandler):
256 __exports__ = ImageHandler.__exports__ + [
257 DBVar('memmap', ty='str'),
258 DBVar('memmap_value', ty='sxpr'),
259 # device channel?
260 ]
262 ostype = "vmx"
263 memmap = None
264 memmap_value = []
265 device_channel = None
266 pid = 0
267 def createImage(self):
268 """Create a VM for the VMX environment.
269 """
270 self.configure()
271 self.parseMemmap()
272 self.createDomain()
274 def buildDomain(self):
275 # Create an event channel
276 self.device_channel = channel.eventChannel(0, self.vm.getDomain())
277 log.info("VMX device model port: %d", self.device_channel.port2)
278 if self.vm.store_channel:
279 store_evtchn = self.vm.store_channel.port2
280 else:
281 store_evtchn = 0
282 ret = xc.vmx_build(dom = self.vm.getDomain(),
283 image = self.kernel,
284 control_evtchn = self.device_channel.port2,
285 store_evtchn = store_evtchn,
286 memsize = self.vm.memory,
287 memmap = self.memmap_value,
288 cmdline = self.cmdline,
289 ramdisk = self.ramdisk,
290 flags = self.flags,
291 vcpus = self.vm.vcpus)
292 if isinstance(ret, dict):
293 self.vm.store_mfn = ret.get('store_mfn')
294 return 0
295 return ret
297 def parseMemmap(self):
298 self.memmap = sxp.child_value(self.vm.config, "memmap")
299 if self.memmap is None:
300 return
301 memmap = sxp.parse(open(self.memmap))[0]
302 from xen.util.memmap import memmap_parse
303 self.memmap_value = memmap_parse(memmap)
305 # Return a list of cmd line args to the device models based on the
306 # xm config file
307 def parseDeviceModelArgs(self):
308 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb',
309 'localtime', 'serial', 'stdvga', 'isa' ]
310 ret = []
311 for a in dmargs:
312 v = sxp.child_value(self.vm.config, a)
314 # python doesn't allow '-' in variable names
315 if a == 'stdvga': a = 'std-vga'
317 # Handle booleans gracefully
318 if a in ['localtime', 'std-vga', 'isa']:
319 if v != None: v = int(v)
321 log.debug("args: %s, val: %s" % (a,v))
322 if v:
323 ret.append("-%s" % a)
324 ret.append("%s" % v)
326 # Handle disk/network related options
327 devices = sxp.children(self.vm.config, 'device')
328 for device in devices:
329 name = sxp.name(sxp.child0(device))
330 if name == 'vbd':
331 vbdinfo = sxp.child(device, 'vbd')
332 uname = sxp.child_value(vbdinfo, 'uname')
333 typedev = sxp.child_value(vbdinfo, 'dev')
334 (vbdtype, vbdparam) = string.split(uname, ':', 1)
335 if re.match('^ioemu:', typedev):
336 (emtype, vbddev) = string.split(typedev, ':', 1)
337 else:
338 emtype = 'vbd'
339 vbddev = typedev
340 if emtype != 'ioemu':
341 continue;
342 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
343 if vbddev not in vbddev_list:
344 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
345 ret.append("-%s" % vbddev)
346 ret.append("%s" % vbdparam)
347 if name == 'vif':
348 vifinfo = sxp.child(device, 'vif')
349 mac = sxp.child_value(vifinfo, 'mac')
350 ret.append("-macaddr")
351 ret.append("%s" % mac)
353 # Handle graphics library related options
354 vnc = sxp.child_value(self.vm.config, 'vnc')
355 sdl = sxp.child_value(self.vm.config, 'sdl')
356 nographic = sxp.child_value(self.vm.config, 'nographic')
357 if nographic:
358 ret.append('-nographic')
359 return ret
361 if vnc and sdl:
362 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
363 elif vnc:
364 ret = ret + ['-vnc', '-k', 'en-us']
365 if vnc:
366 vncport = int(self.vm.getDomain()) + 5900
367 ret = ret + ['-vncport', '%d' % vncport]
368 return ret
370 def createDeviceModel(self):
371 device_model = sxp.child_value(self.vm.config, 'device_model')
372 if not device_model:
373 raise VmError("vmx: missing device model")
374 # Execute device model.
375 #todo: Error handling
376 # XXX RN: note that the order of args matter!
377 args = [device_model]
378 vnc = self.vncParams()
379 if len(vnc):
380 args = args + vnc
381 args = args + ([ "-d", "%d" % self.vm.getDomain(),
382 "-p", "%d" % self.device_channel.port1,
383 "-m", "%s" % self.vm.memory ])
384 args = args + self.parseDeviceModelArgs()
385 env = dict(os.environ)
386 env['DISPLAY'] = sxp.child_value(self.vm.config, 'display')
387 log.info("spawning device models: %s %s", device_model, args)
388 self.pid = os.spawnve(os.P_NOWAIT, device_model, args, env)
389 log.info("device model pid: %d", self.pid)
390 return self.pid
392 def vncParams(self):
393 # see if a vncviewer was specified
394 # XXX RN: bit of a hack. should unify this, maybe stick in config space
395 vncconnect=[]
396 image = self.config
397 args = sxp.child_value(image, "args")
398 if args:
399 arg_list = string.split(args)
400 for arg in arg_list:
401 al = string.split(arg, '=')
402 if al[0] == "VNC_VIEWER":
403 vncconnect=["-vncconnect", "%s" % al[1]]
404 break
405 return vncconnect
407 def destroy(self):
408 channel.eventChannelClose(self.device_channel)
409 import signal
410 if not self.pid:
411 self.pid = self.vm.device_model_pid
412 os.kill(self.pid, signal.SIGKILL)
413 (pid, status) = os.waitpid(self.pid, 0)
414 self.pid = 0
416 def getDomainMemory(self, mem_mb):
417 # for ioreq_t and xenstore
418 static_pages = 2
419 return (mem_mb * 1024) + self.getPageTableSize(mem_mb) + 4 * static_pages
421 def getPageTableSize(self, mem_mb):
422 """Return the size of memory needed for 1:1 page tables for physical
423 mode.
425 @param mem_mb: size in MB
426 @return size in KB
427 """
428 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
429 if os.uname()[4] == 'x86_64':
430 return (5 + ((mem_mb + 1) >> 1)) * 4
431 else:
432 return (1 + ((mem_mb + 3) >> 2)) * 4