devcontroller.py

来自「xen虚拟机源代码安装包」· Python 代码 · 共 695 行 · 第 1/2 页

PY
695
字号
#============================================================================# This library is free software; you can redistribute it and/or# modify it under the terms of version 2.1 of the GNU Lesser General Public# License as published by the Free Software Foundation.## This library is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU# Lesser General Public License for more details.## You should have received a copy of the GNU Lesser General Public# License along with this library; if not, write to the Free Software# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA#============================================================================# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com># Copyright (C) 2005 XenSource Ltd#============================================================================from threading import Eventimport typesfrom xen.xend import sxp, XendOptionsfrom xen.xend.XendError import VmErrorfrom xen.xend.XendLogging import logimport xen.xend.XendConfigfrom xen.xend.xenstore.xstransact import xstransact, completefrom xen.xend.xenstore.xswatch import xswatchimport osDEVICE_CREATE_TIMEOUT  = 100DEVICE_DESTROY_TIMEOUT = 100HOTPLUG_STATUS_NODE = "hotplug-status"HOTPLUG_ERROR_NODE  = "hotplug-error"HOTPLUG_STATUS_ERROR = "error"HOTPLUG_STATUS_BUSY  = "busy"Connected    = 1Error        = 2Missing      = 3Timeout      = 4Busy         = 5Disconnected = 6xenbusState = {    'Unknown'      : 0,    'Initialising' : 1,    'InitWait'     : 2,    'Initialised'  : 3,    'Connected'    : 4,    'Closing'      : 5,    'Closed'       : 6,    'Reconfiguring': 7,    'Reconfigured' : 8,    }xoptions = XendOptions.instance()xenbusState.update(dict(zip(xenbusState.values(), xenbusState.keys())))class DevController:    """Abstract base class for a device controller.  Device controllers create    appropriate entries in the store to trigger the creation, reconfiguration,    and destruction of devices in guest domains.  Each subclass of    DevController is responsible for a particular device-class, and    understands the details of configuration specific to that device-class.    DevController itself provides the functionality common to all device    creation tasks, as well as providing an interface to XendDomainInfo for    triggering those events themselves.    """    # Set when registered.    deviceClass = None    ## public:    def __init__(self, vm):        self.vm = vm        self.hotplug = True    def createDevice(self, config):        """Trigger the creation of a device with the given configuration.        @return The ID for the newly created device.        """        (devid, back, front) = self.getDeviceDetails(config)        if devid is None:            return 0        self.setupDevice(config)        (backpath, frontpath) = self.addStoreEntries(config, devid, back,                                                     front)        import xen.xend.XendDomain        xd = xen.xend.XendDomain.instance()        backdom_name = config.get('backend')        if backdom_name is None:            backdom = xen.xend.XendDomain.DOM0_ID        else:            bd = xd.domain_lookup_nr(backdom_name)            backdom = bd.getDomid()        count = 0        while True:            t = xstransact()            try:                if devid in self.deviceIDs(t):                    if 'dev' in back:                        dev_str = '%s (%d, %s)' % (back['dev'], devid,                                                   self.deviceClass)                    else:                        dev_str = '%s (%s)' % (devid, self.deviceClass)                                        raise VmError("Device %s is already connected." % dev_str)                if count == 0:                    log.debug('DevController: writing %s to %s.',                              str(front), frontpath)                    log.debug('DevController: writing %s to %s.',                              str(xen.xend.XendConfig.scrub_password(back)), backpath)                elif count % 50 == 0:                    log.debug(                      'DevController: still waiting to write device entries.')                t.remove(frontpath)                t.remove(backpath)                t.mkdir(backpath)                t.set_permissions(backpath,                                  {'dom': backdom },                                  {'dom'  : self.vm.getDomid(),                                   'read' : True })                t.mkdir(frontpath)                t.set_permissions(frontpath,                                  {'dom': self.vm.getDomid()},                                  {'dom': backdom, 'read': True})                t.write2(frontpath, front)                t.write2(backpath,  back)                if t.commit():                    return devid                count += 1            except:                t.abort()                raise    def waitForDevices(self):        log.debug("Waiting for devices %s.", self.deviceClass)        return map(self.waitForDevice, self.deviceIDs())    def waitForDevice(self, devid):        log.debug("Waiting for %s.", devid)        if not self.hotplug:            return        (status, err) = self.waitForBackend(devid)        if status == Timeout:            self.destroyDevice(devid, False)            raise VmError("Device %s (%s) could not be connected. "                          "Hotplug scripts not working." %                          (devid, self.deviceClass))        elif status == Error:            self.destroyDevice(devid, False)            if err is None:                raise VmError("Device %s (%s) could not be connected. "                              "Backend device not found." %                              (devid, self.deviceClass))            else:                raise VmError("Device %s (%s) could not be connected. "                              "%s" % (devid, self.deviceClass, err))        elif status == Missing:            # Don't try to destroy the device; it's already gone away.            raise VmError("Device %s (%s) could not be connected. "                          "Device not found." % (devid, self.deviceClass))        elif status == Busy:            self.destroyDevice(devid, False)            if err is None:                err = "Busy."            raise VmError("Device %s (%s) could not be connected.\n%s" %                          (devid, self.deviceClass, err))    def waitForDevice_destroy(self, devid, backpath):        log.debug("Waiting for %s - destroyDevice.", devid)        if not self.hotplug:            return        status = self.waitForBackend_destroy(backpath)        if status == Timeout:            raise VmError("Device %s (%s) could not be disconnected. " %                          (devid, self.deviceClass))    def waitForDevice_reconfigure(self, devid):        log.debug("Waiting for %s - reconfigureDevice.", devid)        (status, err) = self.waitForBackend_reconfigure(devid)        if status == Timeout:            raise VmError("Device %s (%s) could not be reconfigured. " %                          (devid, self.deviceClass))    def reconfigureDevice(self, devid, config):        """Reconfigure the specified device.        The implementation here just raises VmError.  This may be overridden        by those subclasses that can reconfigure their devices.        """        raise VmError('%s devices may not be reconfigured' % self.deviceClass)    def cleanupDeviceOnDomainDestroy(self, devid):        """ Some devices may need special cleanup when the guest domain            is destroyed.        """        return    def destroyDevice(self, devid, force):        """Destroy the specified device.        @param devid The device ID, or something device-specific from which        the device ID can be determined (such as a guest-side device name).        The implementation here simply deletes the appropriate paths from the        store.  This may be overridden by subclasses who need to perform other        tasks on destruction. The implementation here accepts integer device        IDs or paths containg integer deviceIDs, e.g. vfb/0.  Subclasses may        accept other values and convert them to integers before passing them        here.        """        dev = self.convertToDeviceNumber(devid)        self.cleanupDeviceOnDomainDestroy(dev)        # Modify online status /before/ updating state (latter is watched by        # drivers, so this ordering avoids a race).        self.writeBackend(dev, 'online', "0")        self.writeBackend(dev, 'state', str(xenbusState['Closing']))        if force:            frontpath = self.frontendPath(dev)            backpath = xstransact.Read(frontpath, "backend")            if backpath:                xstransact.Remove(backpath)            xstransact.Remove(frontpath)        self.vm._removeVm("device/%s/%d" % (self.deviceClass, dev))    def configurations(self, transaction = None):        return map(lambda x: self.configuration(x, transaction), self.deviceIDs(transaction))    def configuration(self, devid, transaction = None):        """@return an s-expression giving the current configuration of the        specified device.  This would be suitable for giving to {@link        #createDevice} in order to recreate that device."""        configDict = self.getDeviceConfiguration(devid, transaction)        sxpr = [self.deviceClass]        for key, val in configDict.items():            if isinstance(val, (types.ListType, types.TupleType)):                for v in val:                    if v != None:                        sxpr.append([key, v])            else:                if val != None:                    sxpr.append([key, val])        return sxpr    def sxprs(self):        """@return an s-expression describing all the devices of this        controller's device-class.        """        return xstransact.ListRecursive(self.frontendRoot())    def sxpr(self, devid):        """@return an s-expression describing the specified device.        """        return [self.deviceClass, ['dom', self.vm.getDomid(),                                   'id', devid]]    def getDeviceConfiguration(self, devid, transaction = None):        """Returns the configuration of a device.        @note: Similar to L{configuration} except it returns a dict.        @return: dict        """        if transaction is None:            backdomid = xstransact.Read(self.frontendPath(devid), "backend-id")        else:            backdomid = transaction.read(self.frontendPath(devid) + "/backend-id")        if backdomid is None:            raise VmError("Device %s not connected" % devid)        return {'backend': int(backdomid)}    def getAllDeviceConfigurations(self):        all_configs = {}        for devid in self.deviceIDs():            config_dict = self.getDeviceConfiguration(devid)            all_configs[devid] = config_dict        return all_configs    def convertToDeviceNumber(self, devid):        try:            return int(devid)        except ValueError:            # Does devid contain devicetype/deviceid?            # Propogate exception if unable to find an integer devid            return int(type(devid) is str and devid.split('/')[-1] or None)    ## protected:    def getDeviceDetails(self, config):        """Compute the details for creation of a device corresponding to the        given configuration.  These details consist of a tuple of (devID,        backDetails, frontDetails), where devID is the ID for the new device,        and backDetails and frontDetails are the device configuration        specifics for the backend and frontend respectively.        backDetails and frontDetails should be dictionaries, the keys and        values of which will be used as paths in the store.  There is no need        for these dictionaries to include the references from frontend to        backend, nor vice versa, as these will be handled by DevController.        Abstract; must be implemented by every subclass.        @return (devID, backDetails, frontDetails), as specified above.        """        raise NotImplementedError()

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?