diff --git a/Makefile b/Makefile index e1582568..610c303b 100755 --- a/Makefile +++ b/Makefile @@ -204,7 +204,7 @@ install: build ln -sf $$i"SR.py" $$i"SR"; \ done rm $(SM_STAGING)$(SM_DEST)/SHMSR - cd $(SM_STAGING)$(SM_DEST) && rm -f LVMSR && ln -sf LVMSR.py LVHDSR + cd $(SM_STAGING)$(SM_DEST) && rm -f LVMSR && ln -sf LVMSR.py LVMSR cd $(SM_STAGING)$(SM_DEST) && rm -f RawISCSISR && ln -sf RawISCSISR.py ISCSISR cd $(SM_STAGING)$(SM_DEST) && rm -f LVMoISCSISR && ln -sf LVMoISCSISR.py LVMoISCSISR cd $(SM_STAGING)$(SM_DEST) && rm -f LVMoHBASR && ln -sf LVMoHBASR.py LVMoHBASR diff --git a/drivers/FileSR.py b/drivers/FileSR.py index f3ff18da..f7718544 100755 --- a/drivers/FileSR.py +++ b/drivers/FileSR.py @@ -95,7 +95,7 @@ def __init__(self, srcmd, sr_uuid): # "super" sometimes failed due to circular imports SR.SR.__init__(self, srcmd, sr_uuid) self.image_info = {} - self.init_preferred_image_formats() + self._init_preferred_image_formats() self._check_o_direct() @override diff --git a/drivers/LVMSR.py b/drivers/LVMSR.py index 6581ff15..74d95034 100755 --- a/drivers/LVMSR.py +++ b/drivers/LVMSR.py @@ -40,7 +40,7 @@ from refcounter import RefCounter from ipc import IPCFlag from constants import NS_PREFIX_LVM, VG_LOCATION, VG_PREFIX -from cowutil import getCowUtil +from cowutil import getCowUtil, getVdiTypeFromImageFormat from lvmcowutil import LV_PREFIX, LvmCowUtil from lvmanager import LVActivator from vditype import VdiType @@ -155,6 +155,10 @@ def handles(type) -> bool: return type == "ext" return type == LVMSR.DRIVER_TYPE + def __init__(self, srcmd, sr_uuid): + SR.SR.__init__(self, srcmd, sr_uuid) + self._init_preferred_image_formats() + @override def load(self, sr_uuid) -> None: self.ops_exclusive = OPS_EXCLUSIVE @@ -594,7 +598,7 @@ def attach(self, uuid) -> None: self.session, self.sr_ref, scsiutil.devlist_to_serialstring(self.dconf['device'].split(','))) - # Test Legacy Mode Flag and update if VHD volumes exist + # Test Legacy Mode Flag and update if COW volumes exist if self.isMaster and self.legacyMode: vdiInfo = LvmCowUtil.getVDIInfo(self.lvmCache) for uuid, info in vdiInfo.items(): @@ -735,7 +739,7 @@ def scan(self, uuid) -> None: parent = cowutil.getParentNoCheck(lvPath) if parent is not None: - sm_config['vhd-parent'] = parent[len(LV_PREFIX[VdiType.VHD]):] + sm_config['vhd-parent'] = parent[parent.find('-') + 1:] size = cowutil.getSizeVirt(lvPath) if self.provision == "thin": utilisation = util.roundup( @@ -1368,7 +1372,7 @@ def load(self, vdi_uuid) -> None: if "vdi_sm_config" in self.sr.srcmd.params and \ "type" in self.sr.srcmd.params["vdi_sm_config"]: type = self.sr.srcmd.params["vdi_sm_config"]["type"] - + try: self._setType(CREATE_PARAM_TYPES[type]) except: @@ -1376,6 +1380,9 @@ def load(self, vdi_uuid) -> None: if self.sr.legacyMode and self.sr.cmd == 'vdi_create' and VdiType.isCowImage(self.vdi_type): raise xs_errors.XenError('VDICreate', opterr='Cannot create COW type disk in legacy mode') + if not self.vdi_type: + self._setType(getVdiTypeFromImageFormat(self.sr.preferred_image_formats[0])) + self.lvname = "%s%s" % (LV_PREFIX[self.vdi_type], vdi_uuid) self.path = os.path.join(self.sr.path, self.lvname) @@ -1514,7 +1521,7 @@ def attach(self, sr_uuid, vdi_uuid) -> str: if needInflate: try: - self._prepareThin(True) + self._prepareThin(True, self.vdi_type) except: util.logException("attach") raise xs_errors.XenError('LVMProvisionAttach') @@ -1530,7 +1537,7 @@ def detach(self, sr_uuid, vdi_uuid) -> None: util.SMlog("LVMVDI.detach for %s" % self.uuid) self._loadThis() already_deflated = (self.utilisation < \ - LvmCowUtil.calcVolumeSize(self.size)) + self.lvmcowutil.calcVolumeSize(self.size)) needDeflate = True if not VdiType.isCowImage(self.vdi_type) or already_deflated: needDeflate = False @@ -1545,7 +1552,7 @@ def detach(self, sr_uuid, vdi_uuid) -> None: if needDeflate: try: - self._prepareThin(False) + self._prepareThin(False, self.vdi_type) except: util.logException("_prepareThin") raise xs_errors.XenError('VDIUnavailable', opterr='deflate') @@ -1582,7 +1589,7 @@ def resize(self, sr_uuid, vdi_uuid, size) -> str: lvSizeNew = util.roundup(lvutil.LVM_SIZE_INCREMENT, size) else: lvSizeOld = self.utilisation - lvSizeNew = LvmCowUtil.calcVolumeSize(size) + lvSizeNew = self.lvmcowutil.calcVolumeSize(size) if self.sr.provision == "thin": # VDI is currently deflated, so keep it deflated lvSizeNew = lvSizeOld @@ -1724,6 +1731,8 @@ def _snapshot(self, snapType, cloneOp=False, cbtlog=None, cbt_consistency=None): if self.hidden: raise xs_errors.XenError('VDISnapshot', opterr='hidden VDI') + snapVdiType = self.sr._get_snap_vdi_type(self.vdi_type, self.size) + self.sm_config = self.session.xenapi.VDI.get_sm_config( \ self.sr.srcmd.params['vdi_ref']) if "type" in self.sm_config and self.sm_config['type'] == 'raw': @@ -1815,11 +1824,11 @@ def _snapshot(self, snapType, cloneOp=False, cbtlog=None, cbt_consistency=None): self.utilisation = lvSizeBase util.fistpoint.activate("LVHDRT_clone_vdi_after_shrink_parent", self.sr.uuid) - snapVDI = self._createSnap(origUuid, lvSizeOrig, False) + snapVDI = self._createSnap(origUuid, snapVdiType, lvSizeOrig, False) util.fistpoint.activate("LVHDRT_clone_vdi_after_first_snap", self.sr.uuid) snapVDI2 = None if snapType == VDI.SNAPSHOT_DOUBLE: - snapVDI2 = self._createSnap(clonUuid, lvSizeClon, True) + snapVDI2 = self._createSnap(clonUuid, snapVdiType, lvSizeClon, True) # If we have CBT enabled on the VDI, # set CBT status for the new snapshot disk if cbtlog: @@ -1867,9 +1876,10 @@ def _snapshot(self, snapType, cloneOp=False, cbtlog=None, cbt_consistency=None): return self._finishSnapshot(snapVDI, snapVDI2, hostRefs, cloneOp, snapType) - def _createSnap(self, snapUuid, snapSizeLV, isNew): + def _createSnap(self, snapUuid, snapVdiType, snapSizeLV, isNew): """Snapshot self and return the snapshot VDI object""" - snapLV = LV_PREFIX[self.vdi_type] + snapUuid + + snapLV = LV_PREFIX[snapVdiType] + snapUuid snapPath = os.path.join(self.sr.path, snapLV) self.sr.lvmCache.create(snapLV, int(snapSizeLV)) util.fistpoint.activate("LVHDRT_clone_vdi_after_lvcreate", self.sr.uuid) @@ -1893,7 +1903,7 @@ def _createSnap(self, snapUuid, snapSizeLV, isNew): "type", "vdi_type", "vhd-parent", "paused", "relinking", "activating"] and \ not key.startswith("host_"): snapVDI.sm_config[key] = val - snapVDI.sm_config["vdi_type"] = snapType + snapVDI.sm_config["vdi_type"] = snapVdiType snapVDI.sm_config["vhd-parent"] = snapParent snapVDI.lvname = snapLV return snapVDI @@ -2030,8 +2040,8 @@ def _finishSnapshot(self, snapVDI, snapVDI2, hostRefs, cloneOp=False, snapType=N snap = snapVDI return snap.get_params() - def _setType(self, vdiType) -> None: - self.vdi_type = vdiInfo.vdiType + def _setType(self, vdiType: str) -> None: + self.vdi_type = vdiType self.cowutil = getCowUtil(self.vdi_type) self.lvmcowutil = LvmCowUtil(self.cowutil) @@ -2183,7 +2193,7 @@ def _markHidden(self): self.cowutil.setHidden(self.path) self.hidden = 1 - def _prepareThin(self, attach): + def _prepareThin(self, attach, vdiType): origUtilisation = self.sr.lvmCache.getSize(self.lvname) if self.sr.isMaster: # the master can prepare the VDI locally @@ -2198,8 +2208,15 @@ def _prepareThin(self, attach): pools = self.session.xenapi.pool.get_all() master = self.session.xenapi.pool.get_master(pools[0]) rv = self.session.xenapi.host.call_plugin( - master, self.sr.THIN_PLUGIN, fn, - {"srUuid": self.sr.uuid, "vdiUuid": self.uuid}) + master, + self.sr.THIN_PLUGIN, + fn, + { + "srUuid": self.sr.uuid, + "vdiUuid": self.uuid, + "vdiType": vdiType + } + ) util.SMlog("call-plugin returned: %s" % rv) if not rv: raise Exception('plugin %s failed' % self.sr.THIN_PLUGIN) diff --git a/drivers/LinstorSR.py b/drivers/LinstorSR.py index f56f3eae..fa3c5cda 100755 --- a/drivers/LinstorSR.py +++ b/drivers/LinstorSR.py @@ -312,6 +312,10 @@ class LinstorSR(SR.SR): def handles(type) -> bool: return type == LinstorSR.DRIVER_TYPE + def __init__(self, srcmd, sr_uuid): + SR.SR.__init__(self, srcmd, sr_uuid) + self._init_preferred_image_formats() + @override def load(self, sr_uuid) -> None: if not LINSTOR_AVAILABLE: diff --git a/drivers/SR.py b/drivers/SR.py index 9aad2e98..8faee3b4 100755 --- a/drivers/SR.py +++ b/drivers/SR.py @@ -28,7 +28,8 @@ import os import traceback -from cowutil import ImageFormat, parseImageFormats +from cowutil import ImageFormat, getCowUtilFromImageFormat, getVdiTypeFromImageFormat, parseImageFormats +from vditype import VdiType MOUNT_BASE = '/var/run/sr-mount' DEFAULT_TAP = "vhd,qcow2" @@ -520,12 +521,21 @@ def check_dconf(self, key_list, raise_flag=True): return missing_keys - def init_preferred_image_formats(self): + def _init_preferred_image_formats(self) -> None: self.preferred_image_formats = parseImageFormats( self.dconf and self.dconf.get('preferred-image-formats'), DEFAULT_IMAGE_FORMATS ) + def _get_snap_vdi_type(self, vdi_type: str, size: int) -> str: + if VdiType.isCowImage(vdi_type): + return vdi_type + if vdi_type == VdiType.RAW: + for image_format in self.preferred_image_formats: + if getCowUtilFromImageFormat(image_format).canSnapshotRaw(size): + return getVdiTypeFromImageFormat(image_format) + raise xs_errors.XenError('VDISnapshot', opterr=f"cannot snap from `{vdi_type}`") + class ScanRecord: def __init__(self, sr): self.sr = sr diff --git a/drivers/cowutil.py b/drivers/cowutil.py index da4e97a9..e8d87cc7 100755 --- a/drivers/cowutil.py +++ b/drivers/cowutil.py @@ -233,6 +233,10 @@ def snapshot( ) -> None: pass + @abstractmethod + def canSnapshotRaw(self, size: int) -> bool: + pass + @abstractmethod def check( self, @@ -316,10 +320,15 @@ def getVdiTypeFromImageFormat(image_format: ImageFormat) -> str: assert False, f"Unsupported image format: {IMAGE_FORMAT_TO_STR[image_format]}" -def getCowUtil(vdi_type: str) -> CowUtil: +# ------------------------------------------------------------------------------ + +def getCowUtilFromImageFormat(image_format: ImageFormat) -> CowUtil: import vhdutil - if getImageFormatFromVdiType(vdi_type) in (ImageFormat.RAW, ImageFormat.VHD): + if image_format in (ImageFormat.RAW, ImageFormat.VHD): return vhdutil.VhdUtil() assert False, f"Unsupported VDI type: {vdi_type}" + +def getCowUtil(vdi_type: str) -> CowUtil: + return getCowUtilFromImageFormat(getImageFormatFromVdiType(vdi_type)) diff --git a/drivers/lvhd-thin b/drivers/lvhd-thin index fe29aab5..2d66a2ba 100755 --- a/drivers/lvhd-thin +++ b/drivers/lvhd-thin @@ -35,11 +35,12 @@ def attach(session, args): os.environ['LVM_SYSTEM_DIR'] = lvutil.MASTER_LVM_CONF srUuid = args["srUuid"] vdiUuid = args["vdiUuid"] - vgName = "%s%s" % (lvmcowutil.VG_PREFIX, srUuid) + vdiType = args["vdiType"] + vgName = "%s%s" % (VG_PREFIX, srUuid) lvmCache = LVMCache(vgName) journaler = Journaler(lvmCache) try: - lvcowutil.attachThin(journaler, srUuid, vdiUuid, vdiType) + LvmCowUtil(getCowUtil(vdiType)).attachThin(journaler, srUuid, vdiUuid, vdiType) return str(True) except Exception as e: util.logException("lvhd-thin:attach %s" % e) @@ -50,10 +51,11 @@ def detach(session, args): os.environ['LVM_SYSTEM_DIR'] = lvutil.MASTER_LVM_CONF srUuid = args["srUuid"] vdiUuid = args["vdiUuid"] - vgName = "%s%s" % (lvmcowutil.VG_PREFIX, srUuid) + vdiType = args["vdiType"] + vgName = "%s%s" % (VG_PREFIX, srUuid) lvmCache = LVMCache(vgName) try: - lvcowutil.detachThin(session, lvmCache, srUuid, vdiUuid, vdiType) + LvmCowUtil(getCowUtil(vdiType)).detachThin(session, lvmCache, srUuid, vdiUuid, vdiType) return str(True) except Exception as e: util.logException("lvhd-thin:detach %s" % e) diff --git a/drivers/vhdutil.py b/drivers/vhdutil.py index 6647b296..f0bfe22a 100755 --- a/drivers/vhdutil.py +++ b/drivers/vhdutil.py @@ -368,6 +368,10 @@ def snapshot( cmd.append("-e") self._ioretry(cmd) + @override + def canSnapshotRaw(self, size: int) -> bool: + return size <= MAX_VHD_SIZE + @override def check( self,