ia64/xen-unstable

view tools/python/xen/xend/image.py @ 7681:8914eb171c66

After move randomMac to netif in 7637, we need to this for vmx guest.
otherwise, if no mac address configured, vmx guest can not be created.

Signed-off-by: Xiaofeng Ling <xiaofeng.ling@intel.com>
author kaf24@firebug.cl.cam.ac.uk
date Tue Nov 08 12:16:37 2005 +0100 (2005-11-08)
parents eee0489b3a17
children 6a593de2f8ba
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
27 from xen.xend.server.netif import randomMAC
30 xc = xen.lowlevel.xc.new()
33 MAX_GUEST_CMDLINE = 1024
36 def create(vm, imageConfig, deviceConfig):
37 """Create an image handler for a vm.
39 @return ImageHandler instance
40 """
41 return findImageHandlerClass(imageConfig)(vm, imageConfig, deviceConfig)
44 class ImageHandler:
45 """Abstract base class for image handlers.
47 createImage() is called to configure and build the domain from its
48 kernel image and ramdisk etc.
50 The method buildDomain() is used to build the domain, and must be
51 defined in a subclass. Usually this is the only method that needs
52 defining in a subclass.
54 The method createDeviceModel() is called to create the domain device
55 model if it needs one. The default is to do nothing.
57 The method destroy() is called when the domain is destroyed.
58 The default is to do nothing.
59 """
61 ostype = None
64 def __init__(self, vm, imageConfig, deviceConfig):
65 self.vm = vm
67 self.kernel = None
68 self.ramdisk = None
69 self.cmdline = None
71 self.configure(imageConfig, deviceConfig)
73 def configure(self, imageConfig, _):
74 """Config actions common to all unix-like domains."""
76 def get_cfg(name, default = None):
77 return sxp.child_value(imageConfig, name, default)
79 self.kernel = get_cfg("kernel")
80 self.cmdline = ""
81 ip = get_cfg("ip")
82 if ip:
83 self.cmdline += " ip=" + ip
84 root = get_cfg("root")
85 if root:
86 self.cmdline += " root=" + root
87 args = get_cfg("args")
88 if args:
89 self.cmdline += " " + args
90 self.ramdisk = get_cfg("ramdisk", '')
92 self.vm.storeVm(("image/ostype", self.ostype),
93 ("image/kernel", self.kernel),
94 ("image/cmdline", self.cmdline),
95 ("image/ramdisk", self.ramdisk))
98 def handleBootloading(self):
99 self.unlink(self.kernel)
100 self.unlink(self.ramdisk)
103 def unlink(self, f):
104 if not f: return
105 try:
106 os.unlink(f)
107 except OSError, ex:
108 log.warning("error removing bootloader file '%s': %s", f, ex)
111 def createImage(self):
112 """Entry point to create domain memory image.
113 Override in subclass if needed.
114 """
115 return self.createDomain()
118 def createDomain(self):
119 """Build the domain boot image.
120 """
121 # Set params and call buildDomain().
123 if not os.path.isfile(self.kernel):
124 raise VmError('Kernel image does not exist: %s' % self.kernel)
125 if self.ramdisk and not os.path.isfile(self.ramdisk):
126 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
127 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
128 log.warning('kernel cmdline too long, domain %d',
129 self.vm.getDomid())
131 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
132 self.vm.getDomid(), self.vm.getVCpuCount())
134 result = self.buildDomain()
136 if isinstance(result, dict):
137 return result
138 else:
139 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
140 % (self.ostype, self.vm.getDomid(), str(result)))
143 def getDomainMemory(self, mem):
144 """@return The memory required, in KiB, by the domain to store the
145 given amount, also in KiB. This is normally just mem, but VMX domains
146 have overheads to account for."""
147 return mem
149 def buildDomain(self):
150 """Build the domain. Define in subclass."""
151 raise NotImplementedError()
153 def createDeviceModel(self):
154 """Create device model for the domain (define in subclass if needed)."""
155 pass
157 def destroy(self):
158 """Extra cleanup on domain destroy (define in subclass if needed)."""
159 pass
162 class LinuxImageHandler(ImageHandler):
164 ostype = "linux"
166 def buildDomain(self):
167 store_evtchn = self.vm.getStorePort()
168 console_evtchn = self.vm.getConsolePort()
170 log.debug("dom = %d", self.vm.getDomid())
171 log.debug("image = %s", self.kernel)
172 log.debug("store_evtchn = %d", store_evtchn)
173 log.debug("console_evtchn = %d", console_evtchn)
174 log.debug("cmdline = %s", self.cmdline)
175 log.debug("ramdisk = %s", self.ramdisk)
176 log.debug("vcpus = %d", self.vm.getVCpuCount())
178 return xc.linux_build(dom = self.vm.getDomid(),
179 image = self.kernel,
180 store_evtchn = store_evtchn,
181 console_evtchn = console_evtchn,
182 cmdline = self.cmdline,
183 ramdisk = self.ramdisk)
185 class VmxImageHandler(ImageHandler):
187 ostype = "vmx"
189 def configure(self, imageConfig, deviceConfig):
190 ImageHandler.configure(self, imageConfig, deviceConfig)
192 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
193 self.device_model = sxp.child_value(imageConfig, 'device_model')
194 if not self.device_model:
195 raise VmError("vmx: missing device model")
196 self.display = sxp.child_value(imageConfig, 'display')
198 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
199 ("image/device-model", self.device_model),
200 ("image/display", self.display))
202 self.device_channel = None
203 self.pid = 0
205 self.dmargs += self.configVNC(imageConfig)
207 self.lapic = 0
208 lapic = sxp.child_value(imageConfig, 'lapic')
209 if not lapic is None:
210 self.lapic = int(lapic)
212 def buildDomain(self):
213 # Create an event channel
214 self.device_channel = xc.evtchn_alloc_unbound(dom=self.vm.getDomid(),
215 remote_dom=0)
216 log.info("VMX device model port: %d", self.device_channel)
218 store_evtchn = self.vm.getStorePort()
220 log.debug("dom = %d", self.vm.getDomid())
221 log.debug("image = %s", self.kernel)
222 log.debug("control_evtchn = %d", self.device_channel)
223 log.debug("store_evtchn = %d", store_evtchn)
224 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
225 log.debug("lapic = %d", self.lapic)
226 log.debug("vcpus = %d", self.vm.getVCpuCount())
228 return xc.vmx_build(dom = self.vm.getDomid(),
229 image = self.kernel,
230 control_evtchn = self.device_channel,
231 store_evtchn = store_evtchn,
232 memsize = self.vm.getMemoryTarget() / 1024,
233 lapic = self.lapic,
234 vcpus = self.vm.getVCpuCount())
237 # Return a list of cmd line args to the device models based on the
238 # xm config file
239 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
240 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb', 'ne2000',
241 'localtime', 'serial', 'stdvga', 'isa', 'vcpus' ]
242 ret = []
243 for a in dmargs:
244 v = sxp.child_value(imageConfig, a)
246 # python doesn't allow '-' in variable names
247 if a == 'stdvga': a = 'std-vga'
248 if a == 'ne2000': a = 'nic-ne2000'
250 # Handle booleans gracefully
251 if a in ['localtime', 'std-vga', 'isa', 'nic-ne2000']:
252 if v != None: v = int(v)
253 if v: ret.append("-%s" % a)
254 else:
255 if v:
256 ret.append("-%s" % a)
257 ret.append("%s" % v)
258 log.debug("args: %s, val: %s" % (a,v))
260 # Handle disk/network related options
261 for (name, info) in deviceConfig:
262 if name == 'vbd':
263 uname = sxp.child_value(info, 'uname')
264 typedev = sxp.child_value(info, 'dev')
265 (_, vbdparam) = string.split(uname, ':', 1)
266 if re.match('^ioemu:', typedev):
267 (emtype, vbddev) = string.split(typedev, ':', 1)
268 else:
269 emtype = 'vbd'
270 vbddev = typedev
271 if emtype != 'ioemu':
272 continue;
273 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
274 if vbddev not in vbddev_list:
275 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
276 ret.append("-%s" % vbddev)
277 ret.append("%s" % vbdparam)
278 if name == 'vif':
279 mac = sxp.child_value(info, 'mac')
280 if mac == None:
281 mac = randomMAC()
282 ret.append("-macaddr")
283 ret.append("%s" % mac)
284 if name == 'vtpm':
285 instance = sxp.child_value(info, 'instance')
286 ret.append("-instance")
287 ret.append("%s" % instance)
288 return ret
290 def configVNC(self, config):
291 # Handle graphics library related options
292 vnc = sxp.child_value(config, 'vnc')
293 sdl = sxp.child_value(config, 'sdl')
294 ret = []
295 nographic = sxp.child_value(config, 'nographic')
296 if nographic:
297 ret.append('-nographic')
298 return ret
300 if vnc and sdl:
301 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
302 elif vnc:
303 ret = ret + ['-vnc', '-k', 'en-us']
304 if vnc:
305 vncport = int(self.vm.getDomid()) + 5900
306 ret = ret + ['-vncport', '%d' % vncport]
307 return ret
309 def createDeviceModel(self):
310 if self.pid:
311 return
312 # Execute device model.
313 #todo: Error handling
314 # XXX RN: note that the order of args matter!
315 args = [self.device_model]
316 vnc = self.vncParams()
317 if len(vnc):
318 args = args + vnc
319 args = args + ([ "-d", "%d" % self.vm.getDomid(),
320 "-p", "%d" % self.device_channel,
321 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
322 args = args + self.dmargs
323 env = dict(os.environ)
324 if self.display:
325 env['DISPLAY'] = self.display
326 log.info("spawning device models: %s %s", self.device_model, args)
327 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
328 log.info("device model pid: %d", self.pid)
330 def vncParams(self):
331 # see if a vncviewer was specified
332 # XXX RN: bit of a hack. should unify this, maybe stick in config space
333 vncconnect=[]
334 args = self.cmdline
335 if args:
336 arg_list = string.split(args)
337 for arg in arg_list:
338 al = string.split(arg, '=')
339 if al[0] == "VNC_VIEWER":
340 vncconnect=["-vncconnect", "%s" % al[1]]
341 break
342 return vncconnect
344 def destroy(self):
345 import signal
346 if not self.pid:
347 return
348 os.kill(self.pid, signal.SIGKILL)
349 os.waitpid(self.pid, 0)
350 self.pid = 0
352 def getDomainMemory(self, mem):
353 """@see ImageHandler.getDomainMemory"""
354 page_kb = 4
355 if os.uname()[4] == 'ia64':
356 page_kb = 16
357 # for ioreq_t and xenstore
358 static_pages = 2
359 return mem + (self.getPageTableSize(mem / 1024) + static_pages) * page_kb
361 def getPageTableSize(self, mem_mb):
362 """Return the pages of memory needed for 1:1 page tables for physical
363 mode.
365 @param mem_mb: size in MB
366 @return size in KB
367 """
368 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
369 if os.uname()[4] == 'x86_64':
370 return 5 + ((mem_mb + 1) >> 1)
371 elif os.uname()[4] == 'ia64':
372 # 1:1 pgtable is allocated on demand ia64, so just return rom size
373 # for guest firmware
374 return 1024
375 else:
376 return 1 + ((mem_mb + 3) >> 2)
379 """Table of image handler classes for virtual machine images. Indexed by
380 image type.
381 """
382 imageHandlerClasses = {}
385 for h in LinuxImageHandler, VmxImageHandler:
386 imageHandlerClasses[h.ostype] = h
389 def findImageHandlerClass(image):
390 """Find the image handler class for an image config.
392 @param image config
393 @return ImageHandler subclass or None
394 """
395 ty = sxp.name(image)
396 if ty is None:
397 raise VmError('missing image type')
398 imageClass = imageHandlerClasses.get(ty)
399 if imageClass is None:
400 raise VmError('unknown image type: ' + ty)
401 return imageClass