Beispiel #1
0
 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
Beispiel #2
0
    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
Beispiel #4
0
    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