ia64/xen-unstable

view tools/python/xen/xend/image.py @ 5866:22984cc20ff9

Manual merge.
author kaf24@firebug.cl.cam.ac.uk
date Tue Jul 26 16:17:04 2005 +0000 (2005-07-26)
parents c1c9a281eac2 7ac99b43f879
children 2333f6616d18
line source
1 import os, string
3 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
4 from xen.xend import sxp
5 from xen.xend.XendError import VmError
6 from xen.xend.XendLogging import log
7 from xen.xend.xenstore import DBVar
9 from xen.xend.server import channel
11 class ImageHandler:
12 """Abstract base class for image handlers.
14 initDomain() is called to initialise the domain memory.
16 createImage() is called to configure and build the domain from its
17 kernel image and ramdisk etc.
19 The method buildDomain() is used to build the domain, and must be
20 defined in a subclass. Usually this is the only method that needs
21 defining in a subclass.
23 The method createDeviceModel() is called to create the domain device
24 model if it needs one. The default is to do nothing.
26 The method destroy() is called when the domain is destroyed.
27 The default is to do nothing.
29 """
31 #======================================================================
32 # Class vars and methods.
34 """Table of image handler classes for virtual machine images.
35 Indexed by image type.
36 """
37 imageHandlerClasses = {}
39 def addImageHandlerClass(cls, h):
40 """Add a handler class for an image type
41 @param h: handler: ImageHandler subclass
42 """
43 cls.imageHandlerClasses[h.ostype] = h
45 addImageHandlerClass = classmethod(addImageHandlerClass)
47 def findImageHandlerClass(cls, image):
48 """Find the image handler class for an image config.
50 @param image config
51 @return ImageHandler subclass or None
52 """
53 ty = sxp.name(image)
54 if ty is None:
55 raise VmError('missing image type')
56 imageClass = cls.imageHandlerClasses.get(ty)
57 if imageClass is None:
58 raise VmError('unknown image type: ' + ty)
59 return imageClass
61 findImageHandlerClass = classmethod(findImageHandlerClass)
63 def create(cls, vm, image):
64 """Create an image handler for a vm.
66 @param vm vm
67 @param image image config
68 @return ImageHandler instance
69 """
70 imageClass = cls.findImageHandlerClass(image)
71 return imageClass(vm, image)
73 create = classmethod(create)
75 #======================================================================
76 # Instance vars and methods.
78 db = None
79 ostype = None
81 config = None
82 kernel = None
83 ramdisk = None
84 cmdline = None
85 flags = 0
87 __exports__ = [
88 DBVar('ostype', ty='str'),
89 DBVar('config', ty='sxpr'),
90 DBVar('kernel', ty='str'),
91 DBVar('ramdisk', ty='str'),
92 DBVar('cmdline', ty='str'),
93 DBVar('flags', ty='int'),
94 ]
96 def __init__(self, vm, config):
97 self.vm = vm
98 self.db = vm.db.addChild('/image')
99 self.config = config
101 def exportToDB(self, save=False, sync=False):
102 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
104 def importFromDB(self):
105 self.db.importFromDB(self, fields=self.__exports__)
107 def unlink(self, f):
108 if not f: return
109 try:
110 os.unlink(f)
111 except OSError, ex:
112 log.warning("error removing bootloader file '%s': %s", f, ex)
114 def initDomain(self, dom, memory, ssidref, cpu, cpu_weight):
115 """Initial domain create.
117 @return domain id
118 """
120 mem_kb = self.getDomainMemory(memory)
121 if not self.vm.restore:
122 dom = xc.domain_create(dom = dom or 0, ssidref = ssidref)
123 # if bootloader, unlink here. But should go after buildDomain() ?
124 if self.vm.bootloader:
125 self.unlink(self.kernel)
126 self.unlink(self.ramdisk)
127 if dom <= 0:
128 raise VmError('Creating domain failed: name=%s' % self.vm.name)
129 log.debug("initDomain: cpu=%d mem_kb=%d ssidref=%d dom=%d", cpu, mem_kb, ssidref, dom)
130 # xc.domain_setuuid(dom, uuid)
131 xc.domain_setcpuweight(dom, cpu_weight)
132 xc.domain_setmaxmem(dom, mem_kb)
134 try:
135 xc.domain_memory_increase_reservation(dom, mem_kb)
136 except:
137 xc.domain_destroy(dom)
138 raise
140 if cpu != -1:
141 xc.domain_pincpu(dom, 0, 1<<int(cpu))
142 return dom
144 def createImage(self):
145 """Entry point to create domain memory image.
146 Override in subclass if needed.
147 """
148 self.configure()
149 self.createDomain()
151 def configure(self):
152 """Config actions common to all unix-like domains."""
153 self.kernel = sxp.child_value(self.config, "kernel")
154 self.cmdline = ""
155 ip = sxp.child_value(self.config, "ip", None)
156 if ip:
157 self.cmdline += " ip=" + ip
158 root = sxp.child_value(self.config, "root")
159 if root:
160 self.cmdline += " root=" + root
161 args = sxp.child_value(self.config, "args")
162 if args:
163 self.cmdline += " " + args
164 self.ramdisk = sxp.child_value(self.config, "ramdisk", '')
166 def createDomain(self):
167 """Build the domain boot image.
168 """
169 # Set params and call buildDomain().
170 self.flags = 0
171 if self.vm.netif_backend: self.flags |= SIF_NET_BE_DOMAIN
172 if self.vm.blkif_backend: self.flags |= SIF_BLK_BE_DOMAIN
174 if self.vm.recreate or self.vm.restore:
175 return
176 if not os.path.isfile(self.kernel):
177 raise VmError('Kernel image does not exist: %s' % self.kernel)
178 if self.ramdisk and not os.path.isfile(self.ramdisk):
179 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
180 if len(self.cmdline) >= 256:
181 log.warning('kernel cmdline too long, domain %d', self.vm.getDomain())
183 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
184 self.vm.getDomain(), self.vm.vcpus)
185 err = self.buildDomain()
186 if err != 0:
187 raise VmError('Building domain failed: ostype=%s dom=%d err=%d'
188 % (self.ostype, self.vm.getDomain(), err))
190 def getDomainMemory(self, mem_mb):
191 """Memory (in KB) the domain will need for mem_mb (in MB)."""
192 return mem_mb * 1024
194 def buildDomain(self):
195 """Build the domain. Define in subclass."""
196 raise NotImplementedError()
198 def createDeviceModel(self):
199 """Create device model for the domain (define in subclass if needed)."""
200 pass
202 def destroy(self):
203 """Extra cleanup on domain destroy (define in subclass if needed)."""
204 pass
206 addImageHandlerClass = ImageHandler.addImageHandlerClass
208 class LinuxImageHandler(ImageHandler):
210 ostype = "linux"
212 def buildDomain(self):
213 if self.vm.store_channel:
214 store_evtchn = self.vm.store_channel.port2
215 else:
216 store_evtchn = 0
217 ret = xc.linux_build(dom = self.vm.getDomain(),
218 image = self.kernel,
219 control_evtchn = self.vm.channel.getRemotePort(),
220 store_evtchn = store_evtchn,
221 cmdline = self.cmdline,
222 ramdisk = self.ramdisk,
223 flags = self.flags,
224 vcpus = self.vm.vcpus)
225 if isinstance(ret, dict):
226 self.vm.store_mfn = ret.get('store_mfn')
227 return 0
228 return ret
230 class VmxImageHandler(ImageHandler):
232 __exports__ = ImageHandler.__exports__ + [
233 DBVar('memmap', ty='str'),
234 DBVar('memmap_value', ty='sxpr'),
235 # device channel?
236 ]
238 ostype = "vmx"
239 memmap = None
240 memmap_value = []
241 device_channel = None
243 def createImage(self):
244 """Create a VM for the VMX environment.
245 """
246 self.configure()
247 self.parseMemmap()
248 self.createDomain()
250 def buildDomain(self):
251 # Create an event channel
252 self.device_channel = channel.eventChannel(0, self.vm.getDomain())
253 log.info("VMX device model port: %d", self.device_channel.port2)
254 return xc.vmx_build(dom = self.vm.getDomain(),
255 image = self.kernel,
256 control_evtchn = self.device_channel.port2,
257 memsize = self.vm.memory,
258 memmap = self.memmap_value,
259 cmdline = self.cmdline,
260 ramdisk = self.ramdisk,
261 flags = self.flags)
263 def parseMemmap(self):
264 self.memmap = sxp.child_value(self.vm.config, "memmap")
265 if self.memmap is None:
266 return
267 memmap = sxp.parse(open(self.memmap))[0]
268 from xen.util.memmap import memmap_parse
269 self.memmap_value = memmap_parse(memmap)
271 # Return a list of cmd line args to the device models based on the
272 # xm config file
273 def parseDeviceModelArgs(self):
274 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb',
275 'localtime', 'serial', 'macaddr', 'stdvga', 'isa' ]
276 ret = []
277 for a in dmargs:
278 v = sxp.child_value(self.vm.config, a)
280 # python doesn't allow '-' in variable names
281 if a == 'stdvga': a = 'std-vga'
283 # Handle booleans gracefully
284 if a in ['localtime', 'std-vga', 'isa']:
285 if v != None: v = int(v)
287 log.debug("args: %s, val: %s" % (a,v))
288 if v:
289 ret.append("-%s" % a)
290 ret.append("%s" % v)
292 # Handle hd img related options
293 devices = sxp.children(self.vm.config, 'device')
294 for device in devices:
295 vbdinfo = sxp.child(device, 'vbd')
296 if not vbdinfo:
297 raise VmError("vmx: missing vbd configuration")
298 uname = sxp.child_value(vbdinfo, 'uname')
299 vbddev = sxp.child_value(vbdinfo, 'dev')
300 (vbdtype, vbdparam) = string.split(uname, ':', 1)
301 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
302 if vbdtype != 'file' or vbddev not in vbddev_list:
303 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
304 ret.append("-%s" % vbddev)
305 ret.append("%s" % vbdparam)
307 # Handle graphics library related options
308 vnc = sxp.child_value(self.vm.config, 'vnc')
309 sdl = sxp.child_value(self.vm.config, 'sdl')
310 nographic = sxp.child_value(self.vm.config, 'nographic')
311 if nographic:
312 ret.append('-nographic')
313 return ret
315 if vnc and sdl:
316 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
317 elif vnc:
318 ret = ret + ['-vnc', '-k', 'en-us']
319 if vnc:
320 vncport = int(self.vm.getDomain()) + 5900
321 ret = ret + ['-vncport', '%d' % vncport]
322 return ret
324 def createDeviceModel(self):
325 device_model = sxp.child_value(self.vm.config, 'device_model')
326 if not device_model:
327 raise VmError("vmx: missing device model")
328 # Execute device model.
329 #todo: Error handling
330 # XXX RN: note that the order of args matter!
331 args = [device_model]
332 vnc = self.vncParams()
333 if len(vnc):
334 args = args + vnc
335 args = args + ([ "-d", "%d" % self.vm.getDomain(),
336 "-p", "%d" % self.device_channel.port1,
337 "-m", "%s" % self.vm.memory ])
338 args = args + self.parseDeviceModelArgs()
339 env = dict(os.environ)
340 env['DISPLAY'] = sxp.child_value(self.vm.config, 'display')
341 log.info("spawning device models: %s %s", device_model, args)
342 self.pid = os.spawnve(os.P_NOWAIT, device_model, args, env)
343 log.info("device model pid: %d", self.pid)
345 def vncParams(self):
346 # see if a vncviewer was specified
347 # XXX RN: bit of a hack. should unify this, maybe stick in config space
348 vncconnect=[]
349 image = self.config
350 args = sxp.child_value(image, "args")
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 os.kill(self.pid, signal.SIGKILL)
364 (pid, status) = os.waitpid(self.pid, 0)
366 def getDomainMemory(self, mem_mb):
367 return (mem_mb * 1024) + self.getPageTableSize(mem_mb)
369 def getPageTableSize(self, mem_mb):
370 """Return the size of memory needed for 1:1 page tables for physical
371 mode.
373 @param mem_mb: size in MB
374 @return size in KB
375 """
376 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
377 if os.uname()[4] == 'x86_64':
378 return (5 + ((mem_mb + 1) >> 1)) * 4
379 else:
380 return (1 + ((mem_mb + 3) >> 2)) * 4