From d5affeab6a4ba760c696289125b475472d4cd3fa Mon Sep 17 00:00:00 2001 From: "cl349@firebug.cl.cam.ac.uk" Date: Mon, 6 Jun 2005 16:09:57 +0000 Subject: bitkeeper revision 1.1662.1.7 (42a47555156iGoOjUyZtvbuMFZHXkg) XendDomainInfo.py: Move guest image handling into seperate file. image.py: new file Signed-off-by: Mike Wray Signed-off-by: Christian Limpach --- .rootkeys | 1 + tools/python/xen/xend/XendDomainInfo.py | 289 ++++----------------------- tools/python/xen/xend/image.py | 338 ++++++++++++++++++++++++++++++++ 3 files changed, 376 insertions(+), 252 deletions(-) create mode 100644 tools/python/xen/xend/image.py diff --git a/.rootkeys b/.rootkeys index a98c77a58f..c0914886f0 100644 --- a/.rootkeys +++ b/.rootkeys @@ -849,6 +849,7 @@ 40c9c468xzANp6o2D_MeCYwNmOIUsQ tools/python/xen/xend/XendVnet.py 40c9c468x191zetrVlMnExfsQWHxIQ tools/python/xen/xend/__init__.py 40c9c468S2YnCEKmk4ey8XQIST7INg tools/python/xen/xend/encode.py +42a475165HuglqWwNi2fjqNOIHbIKQ tools/python/xen/xend/image.py 4266169ezWIlXSfY50n6HSoVFbosmw tools/python/xen/xend/scheduler.py 40c9c468IxQabrKJSWs0aEjl-27mRQ tools/python/xen/xend/server/SrvConsole.py 40c9c4689Io5bxfbYIfRiUvsiLX0EQ tools/python/xen/xend/server/SrvConsoleDir.py diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index 13c0aa0ef8..f18b331347 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -114,25 +114,6 @@ def get_config_handler(name): """ return config_handlers.get(name) -"""Table of handlers for virtual machine images. -Indexed by image type. -""" -image_handlers = {} - -def add_image_handler(name, h): - """Add a handler for an image type - @param name: image type - @param h: handler: fn(config, name, memory, image) - """ - image_handlers[name] = h - -def get_image_handler(name): - """Get the handler for an image type. - @param name: image type - @return: handler or None - """ - return image_handlers.get(name) - """Table of handlers for devices. Indexed by device type. """ @@ -252,8 +233,6 @@ class XendDomainInfo: self.name = None self.memory = None self.image = None - self.ramdisk = None - self.cmdline = None self.channel = None self.controllers = {} @@ -278,8 +257,6 @@ class XendDomainInfo: self.console_port = None self.savedinfo = None - self.image_handler = None - self.is_vmx = False self.vcpus = 1 self.bootloader = None @@ -330,8 +307,6 @@ class XendDomainInfo: console = self.getConsole() if console: s += " console=" + str(console.console_port) - if self.image: - s += " image=" + self.image s += "" return s @@ -484,8 +459,8 @@ class XendDomainInfo: # Initial domain create. self.setName(sxp.child_value(config, 'name')) self.check_name(self.name) + self.init_image() self.configure_cpus(config) - self.find_image_handler() self.init_domain() self.register_domain() self.configure_bootloader() @@ -527,29 +502,24 @@ class XendDomainInfo: except: raise VmError('invalid vcpus value') - def find_image_handler(self): - """Construct the boot image for the domain. - - @return vm + def init_image(self): + """Create boot image handler for the domain. """ image = sxp.child_value(self.config, 'image') if image is None: raise VmError('missing image') - image_name = sxp.name(image) - if image_name is None: - raise VmError('missing image name') - if image_name == "vmx": - self.is_vmx = True - image_handler = get_image_handler(image_name) - if image_handler is None: - raise VmError('unknown image type: ' + image_name) - self.image_handler = image_handler - return self + self.image = ImageHandler.create(self, image) def construct_image(self): - image = sxp.child_value(self.config, 'image') - self.image_handler(self, image) - return self + """Construct the boot image for the domain. + """ + self.create_channel() + self.image.createImage() + #self.image.exportToDB() + #if self.store_channel: + # self.db.introduceDomain(self.id, + # self.store_mfn, + # self.store_channel) def config_devices(self, name): """Get a list of the 'device' nodes of a given type from the config. @@ -596,7 +566,7 @@ class XendDomainInfo: """Completely destroy the vm. """ self.cleanup() - return self.destroy_domain() + self.destroy_domain() def destroy_domain(self): """Destroy the vm's domain. @@ -606,9 +576,15 @@ class XendDomainInfo: if self.channel: self.channel.close() self.channel = None + if self.image: + try: + self.image.destroy() + self.image = None + except: + pass if self.id is None: return 0 try: - return xc.domain_destroy(dom=self.id) + xc.domain_destroy(dom=self.id) except Exception, err: log.exception("Domain destroy failed: %s", self.name) @@ -661,80 +637,11 @@ class XendDomainInfo: cpu = int(sxp.child_value(self.config, 'cpu', '-1')) except: raise VmError('invalid cpu') - cpu_weight = self.cpu_weight - memory = memory * 1024 + self.pgtable_size(memory) - dom = xc.domain_create(dom= dom) - if self.bootloader: - try: - if kernel: os.unlink(kernel) - if ramdisk: os.unlink(ramdisk) - except OSError, e: - log.warning('unable to unlink kernel/ramdisk: %s' %(e,)) - - if dom <= 0: - raise VmError('Creating domain failed: name=%s memory=%d' - % (self.name, memory)) - xc.domain_setcpuweight(dom, cpu_weight) - xc.domain_setmaxmem(dom, memory) - xc.domain_memory_increase_reservation(dom, memory) - if cpu != -1: - xc.domain_pincpu(dom, 0, 1< Created domain=%d name=%s memory=%d', dom, self.name, memory) - self.setdom(dom) - - def build_domain(self, ostype, kernel, ramdisk, cmdline, memmap): - """Build the domain boot image. - """ - if self.recreate or self.restore: return - if not os.path.isfile(kernel): - raise VmError('Kernel image does not exist: %s' % kernel) - if ramdisk and not os.path.isfile(ramdisk): - raise VmError('Kernel ramdisk does not exist: %s' % ramdisk) - if len(cmdline) >= 256: - log.warning('kernel cmdline too long, domain %d', self.id) - dom = self.id - buildfn = getattr(xc, '%s_build' % ostype) - flags = 0 - if self.netif_backend: flags |= SIF_NET_BE_DOMAIN - if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN - #todo generalise this - if ostype == "vmx": - log.debug('building vmx domain') - err = buildfn(dom = dom, - image = kernel, - control_evtchn = 0, - memsize = self.memory, - memmap = memmap, - cmdline = cmdline, - ramdisk = ramdisk, - flags = flags) - else: - log.debug('building dom with %d vcpus', self.vcpus) - err = buildfn(dom = dom, - image = kernel, - control_evtchn = self.channel.getRemotePort(), - cmdline = cmdline, - ramdisk = ramdisk, - flags = flags, - vcpus = self.vcpus) - if err != 0: - raise VmError('Building domain failed: type=%s dom=%d err=%d' - % (ostype, dom, err)) - - def create_domain(self, ostype, kernel, ramdisk, cmdline, memmap=''): - """Create a domain. Builds the image but does not configure it. - - @param ostype: OS type - @param kernel: kernel image - @param ramdisk: kernel ramdisk - @param cmdline: kernel commandline - """ - - self.create_channel() - self.build_domain(ostype, kernel, ramdisk, cmdline, memmap) - self.image = kernel - self.ramdisk = ramdisk - self.cmdline = cmdline + dom = self.image.initDomain(self.id, self.memory, cpu, self.cpu_weight) + log.debug('init_domain> Created domain=%d name=%s memory=%d', + dom, self.name, self.memory) + if not self.restore: + self.setdom(dom) def create_channel(self): """Create the control channel to the domain. @@ -773,43 +680,6 @@ class XendDomainInfo: ctrl.initController(reboot=True) else: self.create_configured_devices() - if self.is_vmx: - self.create_vmx_model() - - def create_vmx_model(self): - #todo: remove special case for vmx - device_model = sxp.child_value(self.config, 'device_model') - if not device_model: - raise VmError("vmx: missing device model") - device_config = sxp.child_value(self.config, 'device_config') - if not device_config: - raise VmError("vmx: missing device config") - #todo: self.memory? - memory = sxp.child_value(self.config, "memory") - # Create an event channel - device_channel = channel.eventChannel(0, self.id) - # see if a vncviewer was specified - # XXX RN: bit of a hack. should unify this, maybe stick in config space - vncconnect="" - image = sxp.child_value(self.config, "image") - args = sxp.child_value(image, "args") - if args: - arg_list = string.split(args) - for arg in arg_list: - al = string.split(arg, '=') - if al[0] == "VNC_VIEWER": - vncconnect=" -v %s" % al[1] - break - - # Execute device model. - #todo: Error handling - # XXX RN: note that the order of args matter! - os.system(device_model - + " -f %s" % device_config - + vncconnect - + " -d %d" % self.id - + " -p %d" % device_channel['port1'] - + " -m %s" % memory) def device_create(self, dev_config): """Create a new device. @@ -862,9 +732,7 @@ class XendDomainInfo: def configure_bootloader(self): """Configure boot loader. """ - bl = sxp.child_value(self.config, "bootloader") - if bl is not None: - self.bootloader = bl + self.bootloader = sxp.child_value(self.config, "bootloader") def configure_console(self): """Configure the vm console port. @@ -1052,19 +920,6 @@ class XendDomainInfo: log.warning("Unknown config field %s", field_name) index[field_name] = field_index + 1 - def pgtable_size(self, memory): - """Return the size of memory needed for 1:1 page tables for physical - mode. - - @param memory: size in MB - @return size in KB - """ - if self.is_vmx: - # Logic x86-32 specific. - # 1 page for the PGD + 1 pte page for 4MB of memory (rounded) - return (1 + ((memory + 3) >> 2)) * 4 - return 0 - def mem_target_set(self, target): """Set domain memory target in pages. """ @@ -1091,83 +946,6 @@ class XendDomainInfo: return 0 return timeout - (time.time() - self.shutdown_pending['start']) -def vm_image_linux(vm, image): - """Create a VM for a linux image. - - @param name: vm name - @param memory: vm memory - @param image: image config - @return: vm - """ - kernel = sxp.child_value(image, "kernel") - cmdline = "" - ip = sxp.child_value(image, "ip", None) - if ip: - cmdline += " ip=" + ip - root = sxp.child_value(image, "root") - if root: - cmdline += " root=" + root - args = sxp.child_value(image, "args") - if args: - cmdline += " " + args - ramdisk = sxp.child_value(image, "ramdisk", '') - log.debug("creating linux domain with cmdline: %s" %(cmdline,)) - vm.create_domain("linux", kernel, ramdisk, cmdline) - return vm - -def vm_image_plan9(vm, image): - """Create a VM for a Plan 9 image. - - name vm name - memory vm memory - image image config - - returns vm - """ - kernel = sxp.child_value(image, "kernel") - cmdline = "" - ip = sxp.child_value(image, "ip", "dhcp") - if ip: - cmdline += "ip=" + ip - root = sxp.child_value(image, "root") - if root: - cmdline += "root=" + root - args = sxp.child_value(image, "args") - if args: - cmdline += " " + args - ramdisk = sxp.child_value(image, "ramdisk", '') - log.debug("creating plan9 domain with cmdline: %s" %(cmdline,)) - vm.create_domain("plan9", kernel, ramdisk, cmdline) - return vm - -def vm_image_vmx(vm, image): - """Create a VM for the VMX environment. - - @param name: vm name - @param memory: vm memory - @param image: image config - @return: vm - """ - kernel = sxp.child_value(image, "kernel") - cmdline = "" - ip = sxp.child_value(image, "ip", "dhcp") - if ip: - cmdline += " ip=" + ip - root = sxp.child_value(image, "root") - if root: - cmdline += " root=" + root - args = sxp.child_value(image, "args") - if args: - cmdline += " " + args - ramdisk = sxp.child_value(image, "ramdisk", '') - memmap = sxp.child_value(vm.config, "memmap", '') - memmap = sxp.parse(open(memmap))[0] - from xen.util.memmap import memmap_parse - memmap = memmap_parse(memmap) - log.debug("creating vmx domain with cmdline: %s" %(cmdline,)) - vm.create_domain("vmx", kernel, ramdisk, cmdline, memmap) - return vm - def vm_field_ignore(vm, config, val, index): """Dummy config field handler used for fields with built-in handling. @@ -1197,9 +975,16 @@ def vm_field_maxmem(vm, config, val, index): #============================================================================ # Register image handlers. -add_image_handler('linux', vm_image_linux) -add_image_handler('plan9', vm_image_plan9) -add_image_handler('vmx', vm_image_vmx) +from image import \ + addImageHandlerClass, \ + ImageHandler, \ + LinuxImageHandler, \ + Plan9ImageHandler, \ + VmxImageHandler + +addImageHandlerClass(LinuxImageHandler) +addImageHandlerClass(Plan9ImageHandler) +addImageHandlerClass(VmxImageHandler) # Ignore the fields we already handle. add_config_handler('name', vm_field_ignore) diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py new file mode 100644 index 0000000000..09fe390b49 --- /dev/null +++ b/tools/python/xen/xend/image.py @@ -0,0 +1,338 @@ +import os + +import xen.lowlevel.xc; xc = xen.lowlevel.xc.new() +from xen.xend import sxp +from xen.xend.XendError import VmError +from xen.xend.XendLogging import log +#from xen.xend.xenstore import DBVar + +class ImageHandler: + """Abstract base class for image handlers. + + initDomain() is called to initialise the domain memory. + + createImage() is called to configure and build the domain from its + kernel image and ramdisk etc. + + The method buildDomain() is used to build the domain, and must be + defined in a subclass. Usually this is the only method that needs + defining in a subclass. + + The method createDeviceModel() is called to create the domain device + model if it needs one. The default is to do nothing. + + The method destroy() is called when the domain is destroyed. + The default is to do nothing. + + """ + + #====================================================================== + # Class vars and methods. + + """Table of image handler classes for virtual machine images. + Indexed by image type. + """ + imageHandlerClasses = {} + + def addImageHandlerClass(cls, h): + """Add a handler class for an image type + @param h: handler: ImageHandler subclass + """ + cls.imageHandlerClasses[h.ostype] = h + + addImageHandlerClass = classmethod(addImageHandlerClass) + + def findImageHandlerClass(cls, image): + """Find the image handler class for an image config. + + @param image config + @return ImageHandler subclass or None + """ + ty = sxp.name(image) + if ty is None: + raise VmError('missing image type') + imageClass = cls.imageHandlerClasses.get(ty) + if imageClass is None: + raise VmError('unknown image type: ' + ty) + return imageClass + + findImageHandlerClass = classmethod(findImageHandlerClass) + + def create(cls, vm, image): + """Create an image handler for a vm. + + @param vm vm + @param image image config + @return ImageHandler instance + """ + imageClass = cls.findImageHandlerClass(image) + return imageClass(vm, image) + + create = classmethod(create) + + #====================================================================== + # Instance vars and methods. + + #db = None + ostype = None + + config = None + kernel = None + ramdisk = None + cmdline = None + flags = 0 + + #__exports__ = [ + # DBVar('ostype', ty='str'), + # DBVar('config', ty='sxpr'), + # DBVar('kernel', ty='str'), + # DBVar('ramdisk', ty='str'), + # DBVar('cmdline', ty='str'), + # DBVar('flags', ty='int'), + # ] + + def __init__(self, vm, config): + self.vm = vm + #self.db = vm.db.addChild('/image') + self.config = config + + #def exportToDB(self, save=False): + # self.db.exportToDB(self, fields=self.__exports__, save=save) + + #def importFromDB(self): + # self.db.importFromDB(self, fields=self.__exports__) + + def unlink(self, f): + if not f: return + try: + os.unlink(f) + except OSError, ex: + log.warning("error removing bootloader file '%s': %s", f, ex) + + def initDomain(self, dom, memory, cpu, cpu_weight): + """Initial domain create. + + @return domain id + """ + + mem_kb = self.getDomainMemory(memory) + if not self.vm.restore: + dom = xc.domain_create(dom = dom or 0) + # if bootloader, unlink here. But should go after buildDomain() ? + if self.vm.bootloader: + self.unlink(self.kernel) + self.unlink(self.ramdisk) + if dom <= 0: + raise VmError('Creating domain failed: name=%s' % self.vm.name) + log.debug("initDomain: cpu=%d mem_kb=%d dom=%d", cpu, mem_kb, dom) + # xc.domain_setuuid(dom, uuid) + xc.domain_setcpuweight(dom, cpu_weight) + xc.domain_setmaxmem(dom, mem_kb) + xc.domain_memory_increase_reservation(dom, mem_kb) + if cpu != -1: + xc.domain_pincpu(dom, 0, 1<= 256: + log.warning('kernel cmdline too long, domain %d', self.vm.getDomain()) + + log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype, + self.vm.getDomain(), self.vm.vcpus) + err = self.buildDomain() + if err != 0: + raise VmError('Building domain failed: ostype=%s dom=%d err=%d' + % (self.ostype, self.vm.getDomain(), err)) + + def getDomainMemory(self, mem_mb): + """Memory (in KB) the domain will need for mem_mb (in MB).""" + return mem_mb * 1024 + + def buildDomain(self): + """Build the domain. Define in subclass.""" + raise NotImplementedError() + + def createDeviceModel(self): + """Create device model for the domain (define in subclass if needed).""" + pass + + def destroy(self): + """Extra cleanup on domain destroy (define in subclass if needed).""" + pass + +addImageHandlerClass = ImageHandler.addImageHandlerClass + +class LinuxImageHandler(ImageHandler): + + ostype = "linux" + + def buildDomain(self): + #if self.vm.store_channel: + # store_evtchn = self.vm.store_channel.port2 + #else: + # store_evtchn = 0 + d = xc.linux_build(dom = self.vm.getDomain(), + image = self.kernel, + control_evtchn = self.vm.channel.getRemotePort(), + #store_evtchn = store_evtchn, + cmdline = self.cmdline, + ramdisk = self.ramdisk, + flags = self.flags, + vcpus = self.vm.vcpus) + #if isinstance(d, dict): + # self.vm.store_mfn = d.get('store_mfn') + return 0 + +class Plan9ImageHandler(ImageHandler): + + ostype = "plan9" + + def buildDomain(self): + return xc.plan9_build(dom = self.vm.getDomain(), + image = self.kernel, + control_evtchn = self.vm.channel.getRemotePort(), + cmdline = self.cmdline, + ramdisk = self.ramdisk, + flags = self.flags, + vcpus = self.vm.vcpus) + +class VmxImageHandler(ImageHandler): + + #__exports__ = ImageHandler.__exports__ + [ + # DBVar('memmap', ty='str'), + # DBVar('memmap_value', ty='sxpr'), + # # device channel? + # ] + + ostype = "vmx" + memmap = None + memmap_value = None + device_channel = None + + def createImage(self): + """Create a VM for the VMX environment. + """ + self.configure() + self.parseMemmap() + self.createDomain() + + def buildDomain(self): + return xc.vmx_build(dom = self.vm.getDomain(), + image = self.kernel, + control_evtchn = 0, + memsize = self.vm.memory, + memmap = self.memmap_value, + cmdline = self.cmdline, + ramdisk = self.ramdisk, + flags = self.flags) + + def parseMemmap(self): + self.memmap = sxp.child_value(self.vm.config, "memmap") + if self.memmap is None: + raise VmError("missing memmap") + memmap = sxp.parse(open(self.memmap))[0] + from xen.util.memmap import memmap_parse + self.memmap_value = memmap_parse(memmap) + + def createDeviceModel_old(self): + device_model = sxp.child_value(self.vm.config, 'device_model') + if not device_model: + raise VmError("vmx: missing device model") + device_config = sxp.child_value(self.vm.config, 'device_config') + if not device_config: + raise VmError("vmx: missing device config") + # Create an event channel. + self.device_channel = channel.eventChannel(0, self.vm.getDomain()) + # Execute device model. + #todo: Error handling + os.system(device_model + + " -f %s" % device_config + + " -d %d" % self.vm.getDomain() + + " -p %d" % self.device_channel['port1'] + + " -m %s" % self.vm.memory) + + def createDeviceModel(self): + device_model = sxp.child_value(self.vm.config, 'device_model') + if not device_model: + raise VmError("vmx: missing device model") + device_config = sxp.child_value(self.vm.config, 'device_config') + if not device_config: + raise VmError("vmx: missing device config") + # Create an event channel + self.device_channel = channel.eventChannel(0, self.vm.getDomain()) + # Execute device model. + #todo: Error handling + # XXX RN: note that the order of args matter! + os.system(device_model + + " -f %s" % device_config + + self.vncParams() + + " -d %d" % self.vm.getDomain() + + " -p %d" % self.device_channel['port1'] + + " -m %s" % self.vm.memory) + + def vncParams(self): + # see if a vncviewer was specified + # XXX RN: bit of a hack. should unify this, maybe stick in config space + vncconnect="" + image = self.config + args = sxp.child_value(image, "args") + if args: + arg_list = string.split(args) + for arg in arg_list: + al = string.split(arg, '=') + if al[0] == "VNC_VIEWER": + vncconnect=" -v %s" % al[1] + break + return vncconnect + + def destroy(self): + channel.eventChannelClose(self.device_channel) + + def getDomainMemory(self, mem_mb): + return (mem_mb * 1024) + self.getPageTableSize(mem_mb) + + def getPageTableSize(self, mem_mb): + """Return the size of memory needed for 1:1 page tables for physical + mode. + + @param mem_mb: size in MB + @return size in KB + """ + # Logic x86-32 specific. + # 1 page for the PGD + 1 pte page for 4MB of memory (rounded) + return (1 + ((mem_mb + 3) >> 2)) * 4 -- cgit v1.2.3