ia64/xen-unstable

view tools/python/xen/xend/image.py @ 5854:7ac99b43f879

Allow multiple disk images per guest.

Signed-off-by: Yunfeng Zhao <yunfeng.zhao@intel.com>
Signed-off-by: Arun Sharma <arun.sharma@intel.com>
author kaf24@firebug.cl.cam.ac.uk
date Mon Jul 25 21:02:34 2005 +0000 (2005-07-25)
parents edc6e8921dd1
children c1c9a281eac2 22984cc20ff9 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)
133 xc.domain_memory_increase_reservation(dom, mem_kb)
134 if cpu != -1:
135 xc.domain_pincpu(dom, 0, 1<<int(cpu))
136 return dom
138 def createImage(self):
139 """Entry point to create domain memory image.
140 Override in subclass if needed.
141 """
142 self.configure()
143 self.createDomain()
145 def configure(self):
146 """Config actions common to all unix-like domains."""
147 self.kernel = sxp.child_value(self.config, "kernel")
148 self.cmdline = ""
149 ip = sxp.child_value(self.config, "ip", None)
150 if ip:
151 self.cmdline += " ip=" + ip
152 root = sxp.child_value(self.config, "root")
153 if root:
154 self.cmdline += " root=" + root
155 args = sxp.child_value(self.config, "args")
156 if args:
157 self.cmdline += " " + args
158 self.ramdisk = sxp.child_value(self.config, "ramdisk", '')
160 def createDomain(self):
161 """Build the domain boot image.
162 """
163 # Set params and call buildDomain().
164 self.flags = 0
165 if self.vm.netif_backend: self.flags |= SIF_NET_BE_DOMAIN
166 if self.vm.blkif_backend: self.flags |= SIF_BLK_BE_DOMAIN
168 if self.vm.recreate or self.vm.restore:
169 return
170 if not os.path.isfile(self.kernel):
171 raise VmError('Kernel image does not exist: %s' % self.kernel)
172 if self.ramdisk and not os.path.isfile(self.ramdisk):
173 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
174 if len(self.cmdline) >= 256:
175 log.warning('kernel cmdline too long, domain %d', self.vm.getDomain())
177 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
178 self.vm.getDomain(), self.vm.vcpus)
179 err = self.buildDomain()
180 if err != 0:
181 raise VmError('Building domain failed: ostype=%s dom=%d err=%d'
182 % (self.ostype, self.vm.getDomain(), err))
184 def getDomainMemory(self, mem_mb):
185 """Memory (in KB) the domain will need for mem_mb (in MB)."""
186 return mem_mb * 1024
188 def buildDomain(self):
189 """Build the domain. Define in subclass."""
190 raise NotImplementedError()
192 def createDeviceModel(self):
193 """Create device model for the domain (define in subclass if needed)."""
194 pass
196 def destroy(self):
197 """Extra cleanup on domain destroy (define in subclass if needed)."""
198 pass
200 addImageHandlerClass = ImageHandler.addImageHandlerClass
202 class LinuxImageHandler(ImageHandler):
204 ostype = "linux"
206 def buildDomain(self):
207 if self.vm.store_channel:
208 store_evtchn = self.vm.store_channel.port2
209 else:
210 store_evtchn = 0
211 ret = xc.linux_build(dom = self.vm.getDomain(),
212 image = self.kernel,
213 control_evtchn = self.vm.channel.getRemotePort(),
214 store_evtchn = store_evtchn,
215 cmdline = self.cmdline,
216 ramdisk = self.ramdisk,
217 flags = self.flags,
218 vcpus = self.vm.vcpus)
219 if isinstance(ret, dict):
220 self.vm.store_mfn = ret.get('store_mfn')
221 return 0
222 return ret
224 class VmxImageHandler(ImageHandler):
226 __exports__ = ImageHandler.__exports__ + [
227 DBVar('memmap', ty='str'),
228 DBVar('memmap_value', ty='sxpr'),
229 # device channel?
230 ]
232 ostype = "vmx"
233 memmap = None
234 memmap_value = []
235 device_channel = None
237 def createImage(self):
238 """Create a VM for the VMX environment.
239 """
240 self.configure()
241 self.parseMemmap()
242 self.createDomain()
244 def buildDomain(self):
245 # Create an event channel
246 self.device_channel = channel.eventChannel(0, self.vm.getDomain())
247 log.info("VMX device model port: %d", self.device_channel.port2)
248 return xc.vmx_build(dom = self.vm.getDomain(),
249 image = self.kernel,
250 control_evtchn = self.device_channel.port2,
251 memsize = self.vm.memory,
252 memmap = self.memmap_value,
253 cmdline = self.cmdline,
254 ramdisk = self.ramdisk,
255 flags = self.flags)
257 def parseMemmap(self):
258 self.memmap = sxp.child_value(self.vm.config, "memmap")
259 if self.memmap is None:
260 return
261 memmap = sxp.parse(open(self.memmap))[0]
262 from xen.util.memmap import memmap_parse
263 self.memmap_value = memmap_parse(memmap)
265 # Return a list of cmd line args to the device models based on the
266 # xm config file
267 def parseDeviceModelArgs(self):
268 dmargs = [ 'cdrom', 'boot', 'fda', 'fdb',
269 'localtime', 'serial', 'macaddr', 'stdvga', 'isa' ]
270 ret = []
271 for a in dmargs:
272 v = sxp.child_value(self.vm.config, a)
274 # python doesn't allow '-' in variable names
275 if a == 'stdvga': a = 'std-vga'
277 # Handle booleans gracefully
278 if a in ['localtime', 'std-vga', 'isa']:
279 if v != None: v = int(v)
281 log.debug("args: %s, val: %s" % (a,v))
282 if v:
283 ret.append("-%s" % a)
284 ret.append("%s" % v)
286 # Handle hd img related options
287 devices = sxp.children(self.vm.config, 'device')
288 for device in devices:
289 vbdinfo = sxp.child(device, 'vbd')
290 if not vbdinfo:
291 raise VmError("vmx: missing vbd configuration")
292 uname = sxp.child_value(vbdinfo, 'uname')
293 vbddev = sxp.child_value(vbdinfo, 'dev')
294 (vbdtype, vbdparam) = string.split(uname, ':', 1)
295 vbddev_list = ['hda', 'hdb', 'hdc', 'hdd']
296 if vbdtype != 'file' or vbddev not in vbddev_list:
297 raise VmError("vmx: for qemu vbd type=file&dev=hda~hdd")
298 ret.append("-%s" % vbddev)
299 ret.append("%s" % vbdparam)
301 # Handle graphics library related options
302 vnc = sxp.child_value(self.vm.config, 'vnc')
303 sdl = sxp.child_value(self.vm.config, 'sdl')
304 nographic = sxp.child_value(self.vm.config, 'nographic')
305 if nographic:
306 ret.append('-nographic')
307 return ret
309 if vnc and sdl:
310 ret = ret + ['-vnc-and-sdl', '-k', 'en-us']
311 elif vnc:
312 ret = ret + ['-vnc', '-k', 'en-us']
313 if vnc:
314 vncport = int(self.vm.getDomain()) + 5900
315 ret = ret + ['-vncport', '%d' % vncport]
316 return ret
318 def createDeviceModel(self):
319 device_model = sxp.child_value(self.vm.config, 'device_model')
320 if not device_model:
321 raise VmError("vmx: missing device model")
322 # Execute device model.
323 #todo: Error handling
324 # XXX RN: note that the order of args matter!
325 args = [device_model]
326 vnc = self.vncParams()
327 if len(vnc):
328 args = args + vnc
329 args = args + ([ "-d", "%d" % self.vm.getDomain(),
330 "-p", "%d" % self.device_channel.port1,
331 "-m", "%s" % self.vm.memory ])
332 args = args + self.parseDeviceModelArgs()
333 env = dict(os.environ)
334 env['DISPLAY'] = sxp.child_value(self.vm.config, 'display')
335 log.info("spawning device models: %s %s", device_model, args)
336 self.pid = os.spawnve(os.P_NOWAIT, device_model, args, env)
337 log.info("device model pid: %d", self.pid)
339 def vncParams(self):
340 # see if a vncviewer was specified
341 # XXX RN: bit of a hack. should unify this, maybe stick in config space
342 vncconnect=[]
343 image = self.config
344 args = sxp.child_value(image, "args")
345 if args:
346 arg_list = string.split(args)
347 for arg in arg_list:
348 al = string.split(arg, '=')
349 if al[0] == "VNC_VIEWER":
350 vncconnect=["-vncconnect", "%s" % al[1]]
351 break
352 return vncconnect
354 def destroy(self):
355 channel.eventChannelClose(self.device_channel)
356 import signal
357 os.kill(self.pid, signal.SIGKILL)
358 (pid, status) = os.waitpid(self.pid, 0)
360 def getDomainMemory(self, mem_mb):
361 return (mem_mb * 1024) + self.getPageTableSize(mem_mb)
363 def getPageTableSize(self, mem_mb):
364 """Return the size of memory needed for 1:1 page tables for physical
365 mode.
367 @param mem_mb: size in MB
368 @return size in KB
369 """
370 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
371 if os.uname()[4] == 'x86_64':
372 return (5 + ((mem_mb + 1) >> 1)) * 4
373 else:
374 return (1 + ((mem_mb + 3) >> 2)) * 4