ia64/xen-unstable

view tools/python/xen/xend/image.py @ 7456:6f5b94da963a

Avoid specifying bogus DISPLAY=None when spawning device model.

Signed-off-by: James Bulpin <james@xensource.com>
author jrb44@plym.cl.cam.ac.uk
date Thu Oct 20 19:37:41 2005 +0100 (2005-10-20)
parents 8dbf531776e1
children 5a7baecb1c70
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
29 xc = xen.lowlevel.xc.new()
32 MAX_GUEST_CMDLINE = 1024
35 def create(vm, imageConfig, deviceConfig):
36 """Create an image handler for a vm.
38 @return ImageHandler instance
39 """
40 return findImageHandlerClass(imageConfig)(vm, imageConfig, deviceConfig)
43 class ImageHandler:
44 """Abstract base class for image handlers.
46 createImage() is called to configure and build the domain from its
47 kernel image and ramdisk etc.
49 The method buildDomain() is used to build the domain, and must be
50 defined in a subclass. Usually this is the only method that needs
51 defining in a subclass.
53 The method createDeviceModel() is called to create the domain device
54 model if it needs one. The default is to do nothing.
56 The method destroy() is called when the domain is destroyed.
57 The default is to do nothing.
58 """
60 ostype = None
63 def __init__(self, vm, imageConfig, deviceConfig):
64 self.vm = vm
66 self.kernel = None
67 self.ramdisk = None
68 self.cmdline = None
70 self.configure(imageConfig, deviceConfig)
72 def configure(self, imageConfig, _):
73 """Config actions common to all unix-like domains."""
75 def get_cfg(name, default = None):
76 return sxp.child_value(imageConfig, name, default)
78 self.kernel = get_cfg("kernel")
79 self.cmdline = ""
80 ip = get_cfg("ip")
81 if ip:
82 self.cmdline += " ip=" + ip
83 root = get_cfg("root")
84 if root:
85 self.cmdline += " root=" + root
86 args = get_cfg("args")
87 if args:
88 self.cmdline += " " + args
89 self.ramdisk = get_cfg("ramdisk", '')
91 self.vm.storeVm(("image/ostype", self.ostype),
92 ("image/kernel", self.kernel),
93 ("image/cmdline", self.cmdline),
94 ("image/ramdisk", self.ramdisk))
97 def handleBootloading(self):
98 self.unlink(self.kernel)
99 self.unlink(self.ramdisk)
102 def unlink(self, f):
103 if not f: return
104 try:
105 os.unlink(f)
106 except OSError, ex:
107 log.warning("error removing bootloader file '%s': %s", f, ex)
110 def createImage(self):
111 """Entry point to create domain memory image.
112 Override in subclass if needed.
113 """
114 return self.createDomain()
117 def createDomain(self):
118 """Build the domain boot image.
119 """
120 # Set params and call buildDomain().
122 if not os.path.isfile(self.kernel):
123 raise VmError('Kernel image does not exist: %s' % self.kernel)
124 if self.ramdisk and not os.path.isfile(self.ramdisk):
125 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
126 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
127 log.warning('kernel cmdline too long, domain %d',
128 self.vm.getDomid())
130 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
131 self.vm.getDomid(), self.vm.getVCpuCount())
133 result = self.buildDomain()
135 if isinstance(result, dict):
136 return result
137 else:
138 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
139 % (self.ostype, self.vm.getDomid(), str(result)))
142 def getDomainMemory(self, mem):
143 """@return The memory required, in KiB, by the domain to store the
144 given amount, also in KiB. This is normally just mem, but VMX domains
145 have overheads to account for."""
146 return mem
148 def buildDomain(self):
149 """Build the domain. Define in subclass."""
150 raise NotImplementedError()
152 def createDeviceModel(self):
153 """Create device model for the domain (define in subclass if needed)."""
154 pass
156 def destroy(self):
157 """Extra cleanup on domain destroy (define in subclass if needed)."""
158 pass
161 class LinuxImageHandler(ImageHandler):
163 ostype = "linux"
165 def buildDomain(self):
166 store_evtchn = self.vm.getStorePort()
167 console_evtchn = self.vm.getConsolePort()
169 log.debug("dom = %d", self.vm.getDomid())
170 log.debug("image = %s", self.kernel)
171 log.debug("store_evtchn = %d", store_evtchn)
172 log.debug("console_evtchn = %d", console_evtchn)
173 log.debug("cmdline = %s", self.cmdline)
174 log.debug("ramdisk = %s", self.ramdisk)
175 log.debug("vcpus = %d", self.vm.getVCpuCount())
177 return xc.linux_build(dom = self.vm.getDomid(),
178 image = self.kernel,
179 store_evtchn = store_evtchn,
180 console_evtchn = console_evtchn,
181 cmdline = self.cmdline,
182 ramdisk = self.ramdisk)
184 class VmxImageHandler(ImageHandler):
186 ostype = "vmx"
188 def configure(self, imageConfig, deviceConfig):
189 ImageHandler.configure(self, imageConfig, deviceConfig)
191 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
192 self.device_model = sxp.child_value(imageConfig, 'device_model')
193 if not self.device_model:
194 raise VmError("vmx: missing device model")
195 self.display = sxp.child_value(imageConfig, 'display')
197 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
198 ("image/device-model", self.device_model),
199 ("image/display", self.display))
201 self.device_channel = None
202 self.pid = 0
204 self.dmargs += self.configVNC(imageConfig)
207 def buildDomain(self):
208 # Create an event channel
209 self.device_channel = xc.evtchn_alloc_unbound(dom=self.vm.getDomid(),
210 remote_dom=0)
211 log.info("VMX device model port: %d", self.device_channel)
213 store_evtchn = self.vm.getStorePort()
215 log.debug("dom = %d", self.vm.getDomid())
216 log.debug("image = %s", self.kernel)
217 log.debug("control_evtchn = %d", self.device_channel)
218 log.debug("store_evtchn = %d", store_evtchn)
219 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
220 log.debug("vcpus = %d", self.vm.getVCpuCount())
222 return xc.vmx_build(dom = self.vm.getDomid(),
223 image = self.kernel,
224 control_evtchn = self.device_channel,
225 store_evtchn = store_evtchn,
226 memsize = self.vm.getMemoryTarget() / 1024,
227 vcpus = self.vm.getVCpuCount())
230 # Return a list of cmd line args to the device models based on the
231 # xm config file
232 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
233 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb', 'ne2000',
234 'localtime', 'serial', 'stdvga', 'isa', 'vcpus' ]
235 ret = []
236 for a in dmargs:
237 v = sxp.child_value(imageConfig, a)
239 # python doesn't allow '-' in variable names
240 if a == 'stdvga': a = 'std-vga'
241 if a == 'ne2000': a = 'nic-ne2000'
243 # Handle booleans gracefully
244 if a in ['localtime', 'std-vga', 'isa', 'nic-ne2000']:
245 if v != None: v = int(v)
247 log.debug("args: %s, val: %s" % (a,v))
248 if v:
249 ret.append("-%s" % a)
250 ret.append("%s" % v)
252 # Handle disk/network related options
253 for (name, info) in deviceConfig:
254 if name == 'vbd':
255 uname = sxp.child_value(info, 'uname')
256 typedev = sxp.child_value(info, 'dev')
257 (_, vbdparam) = string.split(uname, ':', 1)
258 if re.match('^ioemu:', typedev):
259 (emtype, vbddev) = string.split(typedev, ':', 1)
260 else:
261 emtype = 'vbd'
262 vbddev = typedev
263 if emtype != 'ioemu':
264 continue;
265 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
266 if vbddev not in vbddev_list:
267 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
268 ret.append("-%s" % vbddev)
269 ret.append("%s" % vbdparam)
270 if name == 'vif':
271 mac = sxp.child_value(info, 'mac')
272 ret.append("-macaddr")
273 ret.append("%s" % mac)
274 if name == 'vtpm':
275 instance = sxp.child_value(info, 'instance')
276 ret.append("-instance")
277 ret.append("%s" % instance)
278 return ret
280 def configVNC(self, config):
281 # Handle graphics library related options
282 vnc = sxp.child_value(config, 'vnc')
283 sdl = sxp.child_value(config, 'sdl')
284 ret = []
285 nographic = sxp.child_value(config, 'nographic')
286 if nographic:
287 ret.append('-nographic')
288 return ret
290 if vnc and sdl:
291 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
292 elif vnc:
293 ret = ret + ['-vnc', '-k', 'en-us']
294 if vnc:
295 vncport = int(self.vm.getDomid()) + 5900
296 ret = ret + ['-vncport', '%d' % vncport]
297 return ret
299 def createDeviceModel(self):
300 if self.pid:
301 return
302 # Execute device model.
303 #todo: Error handling
304 # XXX RN: note that the order of args matter!
305 args = [self.device_model]
306 vnc = self.vncParams()
307 if len(vnc):
308 args = args + vnc
309 args = args + ([ "-d", "%d" % self.vm.getDomid(),
310 "-p", "%d" % self.device_channel,
311 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
312 args = args + self.dmargs
313 env = dict(os.environ)
314 if self.display:
315 env['DISPLAY'] = self.display
316 log.info("spawning device models: %s %s", self.device_model, args)
317 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
318 log.info("device model pid: %d", self.pid)
320 def vncParams(self):
321 # see if a vncviewer was specified
322 # XXX RN: bit of a hack. should unify this, maybe stick in config space
323 vncconnect=[]
324 args = self.cmdline
325 if args:
326 arg_list = string.split(args)
327 for arg in arg_list:
328 al = string.split(arg, '=')
329 if al[0] == "VNC_VIEWER":
330 vncconnect=["-vncconnect", "%s" % al[1]]
331 break
332 return vncconnect
334 def destroy(self):
335 import signal
336 if not self.pid:
337 return
338 os.kill(self.pid, signal.SIGKILL)
339 os.waitpid(self.pid, 0)
340 self.pid = 0
342 def getDomainMemory(self, mem):
343 """@see ImageHandler.getDomainMemory"""
344 # for ioreq_t and xenstore
345 static_pages = 2
346 return mem + self.getPageTableSize(mem / 1024) + 4 * static_pages
348 def getPageTableSize(self, mem_mb):
349 """Return the size of memory needed for 1:1 page tables for physical
350 mode.
352 @param mem_mb: size in MB
353 @return size in KB
354 """
355 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
356 if os.uname()[4] == 'x86_64':
357 return (5 + ((mem_mb + 1) >> 1)) * 4
358 elif os.uname()[4] == 'ia64':
359 # XEN/IA64 has p2m table allocated on demand, so only return
360 # guest firmware size here.
361 return 16 * 1024
362 else:
363 return (1 + ((mem_mb + 3) >> 2)) * 4
366 """Table of image handler classes for virtual machine images. Indexed by
367 image type.
368 """
369 imageHandlerClasses = {}
372 for h in LinuxImageHandler, VmxImageHandler:
373 imageHandlerClasses[h.ostype] = h
376 def findImageHandlerClass(image):
377 """Find the image handler class for an image config.
379 @param image config
380 @return ImageHandler subclass or None
381 """
382 ty = sxp.name(image)
383 if ty is None:
384 raise VmError('missing image type')
385 imageClass = imageHandlerClasses.get(ty)
386 if imageClass is None:
387 raise VmError('unknown image type: ' + ty)
388 return imageClass