ia64/xen-unstable

view tools/python/xen/xend/image.py @ 7056:8874b5004ef4

Move ImageHandler registration into image.py from XendDomainInfo.py. Move
ImageHandler.create and findImageHandlerClass to the module level, and inline
addImageHandlerClass. Move ImageHandler.kernel, ramdisk, cmdline, and flags,
which seem inadvertently to have been declared as class variables, and make
them instance variables instead.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@ewan
date Sat Sep 24 20:36:15 2005 +0100 (2005-09-24)
parents ad0270abc9b9
children 74b05ac4706f
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
37 def create(vm, imageConfig, deviceConfig):
38 """Create an image handler for a vm.
40 @return ImageHandler instance
41 """
42 return findImageHandlerClass(imageConfig)(vm, imageConfig, deviceConfig)
45 class ImageHandler:
46 """Abstract base class for image handlers.
48 createImage() is called to configure and build the domain from its
49 kernel image and ramdisk etc.
51 The method buildDomain() is used to build the domain, and must be
52 defined in a subclass. Usually this is the only method that needs
53 defining in a subclass.
55 The method createDeviceModel() is called to create the domain device
56 model if it needs one. The default is to do nothing.
58 The method destroy() is called when the domain is destroyed.
59 The default is to do nothing.
60 """
62 ostype = None
65 def __init__(self, vm, imageConfig, deviceConfig):
66 self.vm = vm
68 self.kernel = None
69 self.ramdisk = None
70 self.cmdline = None
71 self.flags = 0
73 self.configure(imageConfig, deviceConfig)
75 def configure(self, imageConfig, _):
76 """Config actions common to all unix-like domains."""
78 def get_cfg(name, default = None):
79 return sxp.child_value(imageConfig, name, default)
81 self.kernel = get_cfg("kernel")
82 self.cmdline = ""
83 ip = get_cfg("ip")
84 if ip:
85 self.cmdline += " ip=" + ip
86 root = get_cfg("root")
87 if root:
88 self.cmdline += " root=" + root
89 args = get_cfg("args")
90 if args:
91 self.cmdline += " " + args
92 self.ramdisk = get_cfg("ramdisk", '')
94 self.vm.storeVm(("image/ostype", self.ostype),
95 ("image/kernel", self.kernel),
96 ("image/cmdline", self.cmdline),
97 ("image/ramdisk", self.ramdisk))
100 def handleBootloading(self):
101 self.unlink(self.kernel)
102 self.unlink(self.ramdisk)
105 def unlink(self, f):
106 if not f: return
107 try:
108 os.unlink(f)
109 except OSError, ex:
110 log.warning("error removing bootloader file '%s': %s", f, ex)
113 def createImage(self):
114 """Entry point to create domain memory image.
115 Override in subclass if needed.
116 """
117 self.createDomain()
119 def createDomain(self):
120 """Build the domain boot image.
121 """
122 # Set params and call buildDomain().
123 self.flags = self.vm.getBackendFlags()
125 if not os.path.isfile(self.kernel):
126 raise VmError('Kernel image does not exist: %s' % self.kernel)
127 if self.ramdisk and not os.path.isfile(self.ramdisk):
128 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
129 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
130 log.warning('kernel cmdline too long, domain %d',
131 self.vm.getDomid())
133 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
134 self.vm.getDomid(), self.vm.getVCpuCount())
135 err = self.buildDomain()
136 if err != 0:
137 raise VmError('Building domain failed: ostype=%s dom=%d err=%d'
138 % (self.ostype, self.vm.getDomid(), err))
140 def getDomainMemory(self, mem):
141 """@return The memory required, in KiB, by the domain to store the
142 given amount, also in KiB. This is normally just mem, but VMX domains
143 have overheads to account for."""
144 return mem
146 def buildDomain(self):
147 """Build the domain. Define in subclass."""
148 raise NotImplementedError()
150 def createDeviceModel(self):
151 """Create device model for the domain (define in subclass if needed)."""
152 pass
154 def destroy(self):
155 """Extra cleanup on domain destroy (define in subclass if needed)."""
156 pass
158 def set_vminfo(self, d):
159 if d.has_key('store_mfn'):
160 self.vm.setStoreRef(d.get('store_mfn'))
161 if d.has_key('console_mfn'):
162 self.vm.setConsoleRef(d.get('console_mfn'))
165 class LinuxImageHandler(ImageHandler):
167 ostype = "linux"
169 def buildDomain(self):
170 if self.vm.store_channel:
171 store_evtchn = self.vm.store_channel.port2
172 else:
173 store_evtchn = 0
174 if self.vm.console_channel:
175 console_evtchn = self.vm.console_channel.port2
176 else:
177 console_evtchn = 0
179 log.debug("dom = %d", self.vm.getDomid())
180 log.debug("image = %s", self.kernel)
181 log.debug("store_evtchn = %d", store_evtchn)
182 log.debug("console_evtchn = %d", console_evtchn)
183 log.debug("cmdline = %s", self.cmdline)
184 log.debug("ramdisk = %s", self.ramdisk)
185 log.debug("flags = %d", self.flags)
186 log.debug("vcpus = %d", self.vm.getVCpuCount())
188 ret = xc.linux_build(dom = self.vm.getDomid(),
189 image = self.kernel,
190 store_evtchn = store_evtchn,
191 console_evtchn = console_evtchn,
192 cmdline = self.cmdline,
193 ramdisk = self.ramdisk,
194 flags = self.flags,
195 vcpus = self.vm.getVCpuCount())
196 if isinstance(ret, dict):
197 self.set_vminfo(ret)
198 return 0
199 return ret
201 class VmxImageHandler(ImageHandler):
203 ostype = "vmx"
205 def configure(self, imageConfig, deviceConfig):
206 ImageHandler.configure(self, imageConfig, deviceConfig)
208 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
209 self.device_model = sxp.child_value(imageConfig, 'device_model')
210 if not self.device_model:
211 raise VmError("vmx: missing device model")
212 self.display = sxp.child_value(imageConfig, 'display')
214 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
215 ("image/device-model", self.device_model),
216 ("image/display", self.display))
218 self.device_channel = None
219 self.pid = 0
221 self.dmargs += self.configVNC(imageConfig)
224 def createImage(self):
225 """Create a VM for the VMX environment.
226 """
227 self.createDomain()
229 def buildDomain(self):
230 # Create an event channel
231 self.device_channel = channel.eventChannel(0, self.vm.getDomid())
232 log.info("VMX device model port: %d", self.device_channel.port2)
233 if self.vm.store_channel:
234 store_evtchn = self.vm.store_channel.port2
235 else:
236 store_evtchn = 0
238 log.debug("dom = %d", self.vm.getDomid())
239 log.debug("image = %s", self.kernel)
240 log.debug("control_evtchn = %d", self.device_channel.port2)
241 log.debug("store_evtchn = %d", store_evtchn)
242 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
243 log.debug("flags = %d", self.flags)
244 log.debug("vcpus = %d", self.vm.getVCpuCount())
246 ret = xc.vmx_build(dom = self.vm.getDomid(),
247 image = self.kernel,
248 control_evtchn = self.device_channel.port2,
249 store_evtchn = store_evtchn,
250 memsize = self.vm.getMemoryTarget() / 1024,
251 flags = self.flags,
252 vcpus = self.vm.getVCpuCount())
253 if isinstance(ret, dict):
254 self.set_vminfo(ret)
255 return 0
256 return ret
258 # Return a list of cmd line args to the device models based on the
259 # xm config file
260 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
261 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb',
262 'localtime', 'serial', 'stdvga', 'isa', 'vcpus' ]
263 ret = []
264 for a in dmargs:
265 v = sxp.child_value(imageConfig, a)
267 # python doesn't allow '-' in variable names
268 if a == 'stdvga': a = 'std-vga'
270 # Handle booleans gracefully
271 if a in ['localtime', 'std-vga', 'isa']:
272 if v != None: v = int(v)
274 log.debug("args: %s, val: %s" % (a,v))
275 if v:
276 ret.append("-%s" % a)
277 ret.append("%s" % v)
279 # Handle disk/network related options
280 for (name, info) in deviceConfig:
281 if name == 'vbd':
282 uname = sxp.child_value(info, 'uname')
283 typedev = sxp.child_value(info, 'dev')
284 (_, vbdparam) = string.split(uname, ':', 1)
285 if re.match('^ioemu:', typedev):
286 (emtype, vbddev) = string.split(typedev, ':', 1)
287 else:
288 emtype = 'vbd'
289 vbddev = typedev
290 if emtype != 'ioemu':
291 continue;
292 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
293 if vbddev not in vbddev_list:
294 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
295 ret.append("-%s" % vbddev)
296 ret.append("%s" % vbdparam)
297 if name == 'vif':
298 mac = sxp.child_value(info, 'mac')
299 ret.append("-macaddr")
300 ret.append("%s" % mac)
301 if name == 'vtpm':
302 instance = sxp.child_value(info, 'instance')
303 ret.append("-instance")
304 ret.append("%s" % instance)
305 return ret
307 def configVNC(self, config):
308 # Handle graphics library related options
309 vnc = sxp.child_value(config, 'vnc')
310 sdl = sxp.child_value(config, 'sdl')
311 ret = []
312 nographic = sxp.child_value(config, 'nographic')
313 if nographic:
314 ret.append('-nographic')
315 return ret
317 if vnc and sdl:
318 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
319 elif vnc:
320 ret = ret + ['-vnc', '-k', 'en-us']
321 if vnc:
322 vncport = int(self.vm.getDomid()) + 5900
323 ret = ret + ['-vncport', '%d' % vncport]
324 return ret
326 def createDeviceModel(self):
327 if self.pid:
328 return
329 # Execute device model.
330 #todo: Error handling
331 # XXX RN: note that the order of args matter!
332 args = [self.device_model]
333 vnc = self.vncParams()
334 if len(vnc):
335 args = args + vnc
336 args = args + ([ "-d", "%d" % self.vm.getDomid(),
337 "-p", "%d" % self.device_channel.port1,
338 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
339 args = args + self.dmargs
340 env = dict(os.environ)
341 env['DISPLAY'] = self.display
342 log.info("spawning device models: %s %s", self.device_model, args)
343 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
344 log.info("device model pid: %d", self.pid)
346 def vncParams(self):
347 # see if a vncviewer was specified
348 # XXX RN: bit of a hack. should unify this, maybe stick in config space
349 vncconnect=[]
350 args = self.cmdline
351 if args:
352 arg_list = string.split(args)
353 for arg in arg_list:
354 al = string.split(arg, '=')
355 if al[0] == "VNC_VIEWER":
356 vncconnect=["-vncconnect", "%s" % al[1]]
357 break
358 return vncconnect
360 def destroy(self):
361 channel.eventChannelClose(self.device_channel)
362 import signal
363 if not self.pid:
364 return
365 os.kill(self.pid, signal.SIGKILL)
366 os.waitpid(self.pid, 0)
367 self.pid = 0
369 def getDomainMemory(self, mem):
370 """@see ImageHandler.getDomainMemory"""
371 # for ioreq_t and xenstore
372 static_pages = 2
373 return mem + self.getPageTableSize(mem / 1024) + 4 * static_pages
375 def getPageTableSize(self, mem_mb):
376 """Return the size of memory needed for 1:1 page tables for physical
377 mode.
379 @param mem_mb: size in MB
380 @return size in KB
381 """
382 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
383 if os.uname()[4] == 'x86_64':
384 return (5 + ((mem_mb + 1) >> 1)) * 4
385 elif os.uname()[4] == 'ia64':
386 # XEN/IA64 has p2m table allocated on demand, so only return
387 # guest firmware size here.
388 return 16 * 1024
389 else:
390 return (1 + ((mem_mb + 3) >> 2)) * 4
393 """Table of image handler classes for virtual machine images. Indexed by
394 image type.
395 """
396 imageHandlerClasses = {}
399 for h in LinuxImageHandler, VmxImageHandler:
400 imageHandlerClasses[h.ostype] = h
403 def findImageHandlerClass(image):
404 """Find the image handler class for an image config.
406 @param image config
407 @return ImageHandler subclass or None
408 """
409 ty = sxp.name(image)
410 if ty is None:
411 raise VmError('missing image type')
412 imageClass = imageHandlerClasses.get(ty)
413 if imageClass is None:
414 raise VmError('unknown image type: ' + ty)
415 return imageClass