def _restore_snapshot(self, vol_name, source_blockdev): try: snaps_name = source_blockdev.get_name() orig = source_blockdev.get_name() vol = orig[-3:] orig = orig.split('.')[0] orig = orig + vol zfs_snap_name = utils.build_path(self._conf[consts.KEY_VG_NAME], orig) + '@' + snaps_name new_vol = utils.build_path(self._conf[consts.KEY_VG_NAME], vol_name) exec_args = [ self._cmd_create, self.ZVOL_SNAP_CLONE, zfs_snap_name, new_vol ] utils.debug_log_exec_args(self.__class__.__name__, exec_args) zfs_proc = subprocess.Popen(exec_args, 0, self._cmd_create, env=self._subproc_env, close_fds=True) zfs_rc = zfs_proc.wait() if zfs_rc == 0: path = os.path.join(self._conf[self.KEY_DEV_PATH], new_vol) if not self._wait_dev_to_settle(path): raise StoragePluginException except OSError as os_err: logging.error( "Zvol: Snapshot creation failed, unable to run " "external program '%s', error message from the OS: %s" % (self._cmd_create, str(os_err))) raise StoragePluginException retblockdevice = storcore.BlockDevice(vol_name, 0, self._vg_path + vol_name) self._volumes[vol_name] = retblockdevice self.save_state(self._volumes) return retblockdevice
def create_snapshot(self, name, vol_id, source_blockdev): """ Allocates a block device as a snapshot of an existing block device @param name: snapshot name; subject to name constraints @type name: str @param vol_id: volume id @type vol_id: int @param source_blockdev: the existing block device to snapshot @type source_blockdev: BlockDevice object @return: block device of the specified size @rtype: BlockDevice object; None if the allocation fails """ blockdev = None lv_name = source_blockdev.get_name() pool_name = None pool = None try: pool_name = self._pool_lookup[lv_name] pool = self._pools[pool_name] except KeyError: logging.error( "LvmThinPool: Creation of snapshot '%s' of LV '%s' failed: " "Cannot find the associated thin pool" % (name, lv_name) ) try: if pool is not None: snaps_base_name = self.vol_name(name, vol_id) snaps_suffix = pool.extract_pool_name_suffix() snaps_name = snaps_base_name + snaps_suffix tries = 0 while blockdev is None and tries < LvmThinPool.MAX_RETRIES: if tries > 0: try: time.sleep(LvmThinPool.RETRY_DELAY) except OSError: pass tries += 1 self._create_snapshot_impl(snaps_name, lv_name) snaps_exists = self._check_vol_exists(snaps_name) if snaps_exists: size = source_blockdev.get_size_kiB() blockdev = storcore.BlockDevice( snaps_name, size, self._vg_path + snaps_name ) pool.add_volume(snaps_name) self._volumes[snaps_name] = blockdev self._pool_lookup[snaps_name] = pool_name self.up_blockdevice(blockdev) self.save_state([self._pools, self._volumes]) except (StoragePluginCheckFailedException, StoragePluginException): # Unable to run one of the LVM commands # The error is reported by the corresponding function # # Abort pass except StoragePluginUnmanagedVolumeException: # Collision with a volume not managed by drbdmanage logging.error( "LvmThinPool: LV '%s' exists already, but is unknown to " "drbdmanage's storage subsystem. Aborting." % (lv_name) ) except exc.PersistenceException: # save_state() failed # If the snapshot was created, attempt to roll back if blockdev is not None: snaps_name = blockdev.get_name() try: self._remove_vol(snaps_name) except StoragePluginException: pass try: snaps_exists = self._check_vol_exists(snaps_name) if not snaps_exists: blockdev = None try: del self._volumes[snaps_name] except KeyError: pass try: if pool is not None: pool.remove_volume(snaps_name) try: del self._pool_lookup[snaps_name] except KeyError: pass except KeyError: pass pool_exists = self._check_vol_exists(pool_name) if not pool_exists: pool.remove_volume(lv_name) try: del self._pools[pool_name] except KeyError: pass except (StoragePluginCheckFailedException, KeyError): pass except Exception as unhandled_exc: logging.error( "LvmThinPool: Block device creation failed, " "unhandled exception: %s" % (str(unhandled_exc)) ) return blockdev
def _create_snapshot(self, vol_name, source_blockdev): """ Implements snapshot operations (snapshot/restore) @param vol_name: name for the newly created block device @type vol_name: str @param vol_id: volume id @type vol_id: int @param source_blockdev: the existing block device to snapshot @type source_blockdev: BlockDevice object @return: block device of the specified size @rtype: BlockDevice object; None if the allocation fails """ blockdev = None lv_name = source_blockdev.get_name() try: tries = 0 while blockdev is None and tries < self.MAX_RETRIES: if tries > 0: try: time.sleep(self.RETRY_DELAY) except OSError: pass tries += 1 self._create_snapshot_impl(vol_name, lv_name) snaps_exists = self._check_vol_exists(vol_name) if snaps_exists: size = source_blockdev.get_size_kiB() blockdev = storcore.BlockDevice(vol_name, size, self._vg_path + vol_name) self._volumes[vol_name] = blockdev self.up_blockdevice(blockdev) self.save_state(self._volumes) else: logging.warning( "%s: Attempt %d of %d: " "Creation of snapshot volume '%s' failed." % (self.NAME, tries + 1, self.MAX_RETRIES, vol_name)) except (StoragePluginCheckFailedException, StoragePluginException): # Unable to run one of the LV commands # The error is reported by the corresponding function # # Abort pass except StoragePluginUnmanagedVolumeException: # Collision with a volume not managed by drbdmanage logging.error("%s: LV '%s' exists already, but is unknown to " "drbdmanage's storage subsystem. Aborting." % (self.NAME, lv_name)) except exc.PersistenceException: # save_state() failed # If the snapshot was created, attempt to roll back if blockdev is not None: vol_name = blockdev.get_name() try: self._remove_vol(vol_name) except StoragePluginException: pass try: vol_exists = self._check_vol_exists(vol_name) if not vol_exists: blockdev = None try: del self._volumes[vol_name] except KeyError: pass except StoragePluginCheckFailedException: pass except NotImplementedError: raise NotImplementedError except Exception as unhandled_exc: logging.error("%s: Block device creation failed, " "unhandled exception: %s" % (self.NAME, str(unhandled_exc))) return blockdev
def create_blockdevice(self, name, vol_id, size): """ Allocates a block device as backing storage for a DRBD volume @param name: resource name; subject to name constraints @type name: str @param id: volume id @type id: int @param size: size of the block device in kiB (binary kilobytes) @type size: long @return: block device of the specified size @rtype: BlockDevice object; None if the allocation fails """ blockdev = None pool = None # Indicates that the plugin's state needs to be saved save_state_flag = False try: # Calculate the size of the backing thin pool pool_ratio = LvmThinPool.DEFAULT_POOL_RATIO try: pool_ratio = float(self._conf[LvmThinPool.KEY_POOL_RATIO]) # Fall back to the default if the size_ratio really does not # make any sense if pool_ratio <= 0: pool_ratio = LvmThinPool.DEFAULT_POOL_RATIO except ValueError: pass pool_size = long(size * (pool_ratio / 100)) # Generate the volume and pool names lv_name = self.vol_name(name, vol_id) pool_name = ThinPool.generate_pool_name(name, vol_id) # Check for collisions (very unlikely) pool_exists = self._check_vol_exists(pool_name) if not pool_exists: tries = 0 while pool is None and tries < LvmThinPool.MAX_RETRIES: if tries > 0: try: time.sleep(LvmThinPool.RETRY_DELAY) except OSError: pass tries += 1 # Create the thin pool self.__create_pool(pool_name, pool_size) pool_exists = self._check_vol_exists(pool_name) if pool_exists: pool = ThinPool(pool_name, pool_size) self._pools[pool_name] = pool save_state_flag = True else: logging.warning( "LvmThinPool: Attempt %d of %d: " "Creation of pool '%s' failed." % (tries, LvmThinPool.MAX_RETRIES, pool_name) ) if pool_exists: tries = 0 while (blockdev is None and tries < LvmThinPool.MAX_RETRIES): if tries > 0: try: time.sleep(LvmThinPool.RETRY_DELAY) except OSError: pass tries += 1 # Create the logical volume self._create_vol(lv_name, pool_name, size) lv_exists = self._check_vol_exists(lv_name) if lv_exists: # LVM reports that the LV exists, create the # blockdevice object representing the LV in # drbdmanage and register it in the LVM # module's persistent data structures blockdev = storcore.BlockDevice( lv_name, size, self._vg_path + lv_name ) pool.add_volume(lv_name) self._volumes[lv_name] = blockdev self._pool_lookup[lv_name] = pool_name self.up_blockdevice(blockdev) save_state_flag = True else: logging.warning( "LvmThinPool: Attempt %d of %d: " "Creation of LV '%s' failed." % (tries, LvmThinPool.MAX_RETRIES, lv_name) ) else: logging.error( "LvmThinPool: Creation of pool '%s' failed, " "name collision detected. " "(This is commonly a temporary error that is " "automatically resolved later)" % (pool_name) ) except (StoragePluginCheckFailedException, StoragePluginException): # Unable to run one of the LVM commands # The error is reported by the corresponding function # # Abort pass except StoragePluginUnmanagedVolumeException: # Collision with a volume not managed by drbdmanage logging.error( "LvmThinPool: LV '%s' exists already, but is unknown to " "drbdmanage's storage subsystem. Aborting." % (lv_name) ) except Exception as unhandled_exc: logging.error( "LvmThinPool: Block device creation failed, " "unhandled exception: %s" % (str(unhandled_exc)) ) try: if save_state_flag: self.save_state([self._pools, self._volumes]) except exc.PersistenceException: # save_state() failed # If the LV was created, attempt to roll back if blockdev is not None: lv_name = blockdev.get_name() pool_name = pool.get_name() try: self._remove_vol(lv_name) except StoragePluginException: pass try: self._remove_vol(pool_name) except StoragePluginException: pass try: lv_exists = self._check_vol_exists(lv_name) if not lv_exists: blockdev = None try: del self._volumes[lv_name] except KeyError: pass pool_exists = self._check_vol_exists(pool_name) if not pool_exists: pool.remove_volume(lv_name) try: del self._pool_lookup[lv_name] except KeyError: pass try: del self._pools[pool_name] except KeyError: pass except (StoragePluginCheckFailedException, KeyError): pass return blockdev
def create_blockdevice(self, name, vol_id, size): """ Allocates a block device as backing storage for a DRBD volume @param name: resource name; subject to name constraints @type name: str @param id: volume id @type id: int @param size: size of the block device in kiB (binary kilobytes) @type size: long @return: block device of the specified size @rtype: BlockDevice object; None if the allocation fails """ blockdev = None vol_name = self.vol_name(name, vol_id) try: # Remove any existing vol tries = 0 # Check whether an vol with that name exists already vol_exists = self._check_vol_exists(vol_name) if vol_exists: if self._volumes.get(vol_name) is None: # Unknown vol, possibly user-generated and not managed # by drbdmanage. Abort. raise StoragePluginUnmanagedVolumeException logging.warning( "%s: Volume '%s' exists already, attempting to remove it." % (self.NAME, vol_name)) while vol_exists and tries < self.MAX_RETRIES: if tries > 0: try: time.sleep(self.RETRY_DELAY) except OSError: pass # vol exists, maybe from an earlier attempt at creating # the volume. Remove existing vol and recreate it. logging.warning( "%s: Attempt %d of %d: " "Removal of volume '%s' failed." % (self.NAME, tries + 1, self.MAX_RETRIES, vol_name)) self._remove_vol(vol_name) # Check whether the removal was successful vol_exists = self._check_vol_exists(vol_name) if not vol_exists: try: del self._volumes[vol_name] except KeyError: pass self.save_state(self._volumes) tries += 1 # Create the vol, unless the removal of any existing vol under # the specified name was unsuccessful if not vol_exists: tries = 0 while blockdev is None and tries < self.MAX_RETRIES: if tries > 0: try: time.sleep(self.RETRY_DELAY) except OSError: pass self._create_vol(vol_name, size) vol_exists = self._check_vol_exists(vol_name) if vol_exists: # volM reports that the vol exists, create the # blockdevice object representing the vol in # drbdmanage and register it in the volM module's # persistent data structures blockdev = storcore.BlockDevice( vol_name, size, self._vg_path + vol_name) self._volumes[vol_name] = blockdev self.save_state(self._volumes) else: logging.error( "%s: Attempt %d of %d: " "Creation of vol '%s' failed." % (self.NAME, tries + 1, self.MAX_RETRIES, vol_name)) tries += 1 else: logging.error( "%s: Removal of an existing volume '%s' failed. " "Unable to clean up and recreate the volume, using existing volume." % (self.NAME, vol_name)) # For whatever reason an existing volume is there, and the OS is # incapable of cleaning up the mess, therefore just use the # existing volume blockdev = storcore.BlockDevice(vol_name, size, self._vg_path + vol_name) self._volumes[vol_name] = blockdev self.save_state(self._volumes) except (StoragePluginCheckFailedException, StoragePluginException): # Unable to run one of the volM commands # The error is reported by the corresponding function # # Abort pass except exc.PersistenceException: # save_state() failed # If the vol was created, attempt to roll back if blockdev is not None: try: self._remove_vol(vol_name) except StoragePluginException: pass try: vol_exists = self._check_vol_exists(vol_name) if not vol_exists: blockdev = None del self._volumes[vol_name] except (StoragePluginCheckFailedException, KeyError): pass except StoragePluginUnmanagedVolumeException: # Collision with a volume not managed by drbdmanage logging.error("%s: vol '%s' exists already, but is unknown to " "drbdmanage's storage subsystem. Aborting." % (self.NAME, vol_name)) except Exception as unhandled_exc: logging.error("%s: Block device creation failed, " "unhandled exception: %s" % (self.NAME, str(unhandled_exc))) return blockdev