Esempio n. 1
0
    def _check_restore_vol_size(self, backup_base, restore_vol, restore_length,
                                src_pool):
        """Ensure that the restore volume is the correct size.

        If the restore volume was bigger than the backup, the diff restore will
        shrink it to the size of the original backup so we need to
        post-process and resize it back to its expected size.
        """
        with rbd_driver.RADOSClient(self, self._ceph_backup_pool) as client:
            adjust_size = 0
            base_image = self.rbd.Image(client.ioctx,
                                        strutils.safe_encode(backup_base),
                                        read_only=True)
            try:
                if restore_length != base_image.size():
                    adjust_size = restore_length
            finally:
                base_image.close()

        if adjust_size:
            with rbd_driver.RADOSClient(self, src_pool) as client:
                dest_image = self.rbd.Image(client.ioctx,
                                            strutils.safe_encode(restore_vol))
                try:
                    LOG.debug(_("Adjusting restore vol size"))
                    dest_image.resize(adjust_size)
                finally:
                    dest_image.close()
Esempio n. 2
0
    def _check_restore_vol_size(self, backup_base, restore_vol, restore_length,
                                src_pool):
        """Ensure that the restore volume is the correct size.

        If the restore volume was bigger than the backup, the diff restore will
        shrink it to the size of the original backup so we need to
        post-process and resize it back to its expected size.
        """
        with rbd_driver.RADOSClient(self, self._ceph_backup_pool) as client:
            adjust_size = 0
            base_image = self.rbd.Image(client.ioctx,
                                        strutils.safe_encode(backup_base),
                                        read_only=True)
            try:
                if restore_length != base_image.size():
                    adjust_size = restore_length
            finally:
                base_image.close()

        if adjust_size:
            with rbd_driver.RADOSClient(self, src_pool) as client:
                dest_image = self.rbd.Image(client.ioctx,
                                            strutils.safe_encode(restore_vol))
                try:
                    LOG.debug("Adjusting restore vol size")
                    dest_image.resize(adjust_size)
                finally:
                    dest_image.close()
Esempio n. 3
0
    def _rbd_diff_transfer(self,
                           src_name,
                           src_pool,
                           dest_name,
                           dest_pool,
                           src_user,
                           src_conf,
                           dest_user,
                           dest_conf,
                           src_snap=None,
                           from_snap=None):
        """Copy only extents changed between two points.

        If no snapshot is provided, the diff extents will be all those changed
        since the rbd volume/base was created, otherwise it will be those
        changed since the snapshot was created.
        """
        LOG.debug(
            _("Performing differential transfer from '%(src)s' to "
              "'%(dest)s'") % {
                  'src': src_name,
                  'dest': dest_name
              })

        # NOTE(dosaboy): Need to be tolerant of clusters/clients that do
        # not support these operations since at the time of writing they
        # were very new.

        src_ceph_args = self._ceph_args(src_user, src_conf, pool=src_pool)
        dest_ceph_args = self._ceph_args(dest_user, dest_conf, pool=dest_pool)

        cmd1 = ['rbd', 'export-diff'] + src_ceph_args
        if from_snap is not None:
            cmd1.extend(['--from-snap', from_snap])
        if src_snap:
            path = strutils.safe_encode("%s/%s@%s" %
                                        (src_pool, src_name, src_snap))
        else:
            path = strutils.safe_encode("%s/%s" % (src_pool, src_name))

        cmd1.extend([path, '-'])

        cmd2 = ['rbd', 'import-diff'] + dest_ceph_args
        rbd_path = strutils.safe_encode("%s/%s" % (dest_pool, dest_name))
        cmd2.extend(['-', rbd_path])

        ret, stderr = self._piped_execute(cmd1, cmd2)
        if ret:
            msg = (_("RBD diff op failed - (ret=%(ret)s stderr=%(stderr)s)") %
                   ({
                       'ret': ret,
                       'stderr': stderr
                   }))
            LOG.info(msg)
            raise exception.BackupRBDOperationFailed(msg)
Esempio n. 4
0
 def _clone(self, volume, src_pool, src_image, src_snap):
     LOG.debug('cloning %(pool)s/%(img)s@%(snap)s to %(dst)s' % dict(
         pool=src_pool, img=src_image, snap=src_snap, dst=volume['name']))
     with RADOSClient(self, src_pool) as src_client:
         with RADOSClient(self) as dest_client:
             self.rbd.RBD().clone(src_client.ioctx,
                                  strutils.safe_encode(src_image),
                                  strutils.safe_encode(src_snap),
                                  dest_client.ioctx,
                                  strutils.safe_encode(volume['name']),
                                  features=self.rbd.RBD_FEATURE_LAYERING)
Esempio n. 5
0
 def _clone(self, volume, src_pool, src_image, src_snap):
     LOG.debug('cloning %(pool)s/%(img)s@%(snap)s to %(dst)s' %
               dict(pool=src_pool, img=src_image, snap=src_snap,
                    dst=volume['name']))
     with RADOSClient(self, src_pool) as src_client:
         with RADOSClient(self) as dest_client:
             self.rbd.RBD().clone(src_client.ioctx,
                                  strutils.safe_encode(src_image),
                                  strutils.safe_encode(src_snap),
                                  dest_client.ioctx,
                                  strutils.safe_encode(volume['name']),
                                  features=self.rbd.RBD_FEATURE_LAYERING)
Esempio n. 6
0
 def delete_snapshot(self, snapshot):
     """Deletes an rbd snapshot."""
     # NOTE(dosaboy): this was broken by commit cbe1d5f. Ensure names are
     #                utf-8 otherwise librbd will barf.
     volume_name = strutils.safe_encode(snapshot['volume_name'])
     snap_name = strutils.safe_encode(snapshot['name'])
     with RBDVolumeProxy(self, volume_name) as volume:
         if self._supports_layering():
             try:
                 volume.unprotect_snap(snap_name)
             except self.rbd.ImageBusy:
                 raise exception.SnapshotIsBusy(snapshot_name=snap_name)
         volume.remove_snap(snap_name)
Esempio n. 7
0
 def delete_snapshot(self, snapshot):
     """Deletes an rbd snapshot."""
     # NOTE(dosaboy): this was broken by commit cbe1d5f. Ensure names are
     #                utf-8 otherwise librbd will barf.
     volume_name = strutils.safe_encode(snapshot['volume_name'])
     snap_name = strutils.safe_encode(snapshot['name'])
     with RBDVolumeProxy(self, volume_name) as volume:
         if self._supports_layering():
             try:
                 volume.unprotect_snap(snap_name)
             except self.rbd.ImageBusy:
                 raise exception.SnapshotIsBusy(snapshot_name=snap_name)
         volume.remove_snap(snap_name)
Esempio n. 8
0
File: ceph.py Progetto: dlq84/cinder
    def _get_backup_base_name(self, volume_id, backup_id=None, diff_format=False):
        """Return name of base image used for backup.

        Incremental backups use a new base name so we support old and new style
        format.
        """
        # Ensure no unicode
        if diff_format:
            return strutils.safe_encode("volume-%s.backup.base" % volume_id)
        else:
            if backup_id is None:
                msg = _("Backup id required")
                raise exception.InvalidParameterValue(msg)
            return strutils.safe_encode("volume-%s.backup.%s" % (volume_id, backup_id))
Esempio n. 9
0
    def __init__(self, driver, name, pool=None, snapshot=None, read_only=False):
        client, ioctx = driver._connect_to_rados(pool)
        if snapshot is not None:
            snapshot = strutils.safe_encode(snapshot)

        try:
            self.volume = driver.rbd.Image(ioctx, strutils.safe_encode(name), snapshot=snapshot, read_only=read_only)
        except driver.rbd.Error:
            LOG.exception(_("error opening rbd image %s"), name)
            driver._disconnect_from_rados(client, ioctx)
            raise
        self.driver = driver
        self.client = client
        self.ioctx = ioctx
Esempio n. 10
0
File: ceph.py Progetto: dlq84/cinder
    def _rbd_diff_transfer(
        self,
        src_name,
        src_pool,
        dest_name,
        dest_pool,
        src_user,
        src_conf,
        dest_user,
        dest_conf,
        src_snap=None,
        from_snap=None,
    ):
        """Copy only extents changed between two points.

        If no snapshot is provided, the diff extents will be all those changed
        since the rbd volume/base was created, otherwise it will be those
        changed since the snapshot was created.
        """
        LOG.debug(
            "Performing differential transfer from '%(src)s' to " "'%(dest)s'" % {"src": src_name, "dest": dest_name}
        )

        # NOTE(dosaboy): Need to be tolerant of clusters/clients that do
        # not support these operations since at the time of writing they
        # were very new.

        src_ceph_args = self._ceph_args(src_user, src_conf, pool=src_pool)
        dest_ceph_args = self._ceph_args(dest_user, dest_conf, pool=dest_pool)

        cmd1 = ["rbd", "export-diff"] + src_ceph_args
        if from_snap is not None:
            cmd1.extend(["--from-snap", from_snap])
        if src_snap:
            path = strutils.safe_encode("%s/%s@%s" % (src_pool, src_name, src_snap))
        else:
            path = strutils.safe_encode("%s/%s" % (src_pool, src_name))

        cmd1.extend([path, "-"])

        cmd2 = ["rbd", "import-diff"] + dest_ceph_args
        rbd_path = strutils.safe_encode("%s/%s" % (dest_pool, dest_name))
        cmd2.extend(["-", rbd_path])

        ret, stderr = self._piped_execute(cmd1, cmd2)
        if ret:
            msg = _("RBD diff op failed - (ret=%(ret)s stderr=%(stderr)s)") % {"ret": ret, "stderr": stderr}
            LOG.info(msg)
            raise exception.BackupRBDOperationFailed(msg)
Esempio n. 11
0
    def _get_backup_base_name(self, volume_id, backup_id=None,
                              diff_format=False):
        """Return name of base image used for backup.

        Incremental backups use a new base name so we support old and new style
        format.
        """
        # Ensure no unicode
        if diff_format:
            return strutils.safe_encode("volume-%s.backup.base" % volume_id)
        else:
            if backup_id is None:
                msg = _("Backup id required")
                raise exception.InvalidParameterValue(msg)
            return strutils.safe_encode("volume-%s.backup.%s" %
                                        (volume_id, backup_id))
Esempio n. 12
0
 def create_snapshot(self, snapshot):
     """Creates an rbd snapshot."""
     with RBDVolumeProxy(self, snapshot['volume_name']) as volume:
         snap = strutils.safe_encode(snapshot['name'])
         volume.create_snap(snap)
         if self._supports_layering():
             volume.protect_snap(snap)
Esempio n. 13
0
 def create_snapshot(self, snapshot):
     """Creates an rbd snapshot."""
     with RBDVolumeProxy(self, snapshot['volume_name']) as volume:
         snap = strutils.safe_encode(snapshot['name'])
         volume.create_snap(snap)
         if self._supports_layering():
             volume.protect_snap(snap)
Esempio n. 14
0
    def manage_existing(self, volume, existing_ref):
        """Manages an existing image.

        Renames the image name to match the expected name for the volume.
        Error checking done by manage_existing_get_size is not repeated.

        :param volume:
            volume ref info to be set
        :param existing_ref:
            existing_ref is a dictionary of the form:
            {'source-name': <name of rbd image>}
        """
        # Raise an exception if we didn't find a suitable rbd image.
        with RADOSClient(self) as client:
            rbd_name = existing_ref['source-name']
            self.rbd.RBD().rename(client.ioctx, strutils.safe_encode(rbd_name),
                                  strutils.safe_encode(volume['name']))
Esempio n. 15
0
    def manage_existing(self, volume, existing_ref):
        """Manages an existing image.

        Renames the image name to match the expected name for the volume.
        Error checking done by manage_existing_get_size is not repeated.

        :param volume:
            volume ref info to be set
        :param existing_ref:
            existing_ref is a dictionary of the form:
            {'source-name': <name of rbd image>}
        """
        # Raise an exception if we didn't find a suitable rbd image.
        with RADOSClient(self) as client:
            rbd_name = existing_ref['source-name']
            self.rbd.RBD().rename(client.ioctx, strutils.safe_encode(rbd_name),
                                  strutils.safe_encode(volume['name']))
Esempio n. 16
0
File: ceph.py Progetto: dlq84/cinder
    def __init__(self, context, db_driver=None, execute=None):
        super(CephBackupDriver, self).__init__(context, db_driver)
        self.rbd = rbd
        self.rados = rados
        self.chunk_size = CONF.backup_ceph_chunk_size
        self._execute = execute or utils.execute

        if self._supports_stripingv2:
            self.rbd_stripe_unit = CONF.backup_ceph_stripe_unit
            self.rbd_stripe_count = CONF.backup_ceph_stripe_count
        else:
            LOG.info(_("RBD striping not supported - ignoring configuration " "settings for rbd striping"))
            self.rbd_stripe_count = 0
            self.rbd_stripe_unit = 0

        self._ceph_backup_user = strutils.safe_encode(CONF.backup_ceph_user)
        self._ceph_backup_pool = strutils.safe_encode(CONF.backup_ceph_pool)
        self._ceph_backup_conf = strutils.safe_encode(CONF.backup_ceph_conf)
Esempio n. 17
0
    def __init__(self, context, db_driver=None, execute=None):
        super(CephBackupDriver, self).__init__(context, db_driver)
        self.rbd = rbd
        self.rados = rados
        self.chunk_size = CONF.backup_ceph_chunk_size
        self._execute = execute or utils.execute

        if self._supports_stripingv2:
            self.rbd_stripe_unit = CONF.backup_ceph_stripe_unit
            self.rbd_stripe_count = CONF.backup_ceph_stripe_count
        else:
            LOG.info(_("RBD striping not supported - ignoring configuration "
                       "settings for rbd striping"))
            self.rbd_stripe_count = 0
            self.rbd_stripe_unit = 0

        self._ceph_backup_user = strutils.safe_encode(CONF.backup_ceph_user)
        self._ceph_backup_pool = strutils.safe_encode(CONF.backup_ceph_pool)
        self._ceph_backup_conf = strutils.safe_encode(CONF.backup_ceph_conf)
Esempio n. 18
0
File: ceph.py Progetto: dlq84/cinder
 def _connect_to_rados(self, pool=None):
     """Establish connection to the backup Ceph cluster."""
     client = self.rados.Rados(rados_id=self._ceph_backup_user, conffile=self._ceph_backup_conf)
     try:
         client.connect()
         pool_to_open = strutils.safe_encode(pool or self._ceph_backup_pool)
         ioctx = client.open_ioctx(pool_to_open)
         return client, ioctx
     except self.rados.Error:
         # shutdown cannot raise an exception
         client.shutdown()
         raise
Esempio n. 19
0
 def __init__(self, *args, **kwargs):
     super(CephIscsiDriver, self).__init__(*args, **kwargs)
     self.configuration.append_config_values(rbd_opts)
     self.configuration.append_config_values(ceph_iscsi_opts)
     self.rados = kwargs.get('rados', rados)
     self.rbd = kwargs.get('rbd', rbd)
     self._stats = {}
     self.image_scan_attempts = IMAGE_SCAN_ATTEMPTS_DEFAULT
     for attr in ['rbd_user', 'rbd_ceph_conf', 'rbd_pool']:
         val = getattr(self.configuration, attr)
         if val is not None:
             setattr(self.configuration, attr, strutils.safe_encode(val))
Esempio n. 20
0
 def __init__(self, *args, **kwargs):
     super(CephIscsiDriver, self).__init__(*args, **kwargs)
     self.configuration.append_config_values(rbd_opts)
     self.configuration.append_config_values(ceph_iscsi_opts)
     self.rados = kwargs.get('rados', rados)
     self.rbd = kwargs.get('rbd', rbd)
     self._stats = {}
     self.image_scan_attempts = IMAGE_SCAN_ATTEMPTS_DEFAULT
     for attr in ['rbd_user', 'rbd_ceph_conf', 'rbd_pool']:
         val = getattr(self.configuration, attr)
         if val is not None:
             setattr(self.configuration, attr, strutils.safe_encode(val))
Esempio n. 21
0
    def __init__(self,
                 driver,
                 name,
                 pool=None,
                 snapshot=None,
                 read_only=False):
        client, ioctx = driver._connect_to_rados(pool)
        if snapshot is not None:
            snapshot = strutils.safe_encode(snapshot)

        try:
            self.volume = driver.rbd.Image(ioctx,
                                           strutils.safe_encode(name),
                                           snapshot=snapshot,
                                           read_only=read_only)
        except driver.rbd.Error:
            LOG.exception(_("error opening rbd image %s"), name)
            driver._disconnect_from_rados(client, ioctx)
            raise
        self.driver = driver
        self.client = client
        self.ioctx = ioctx
Esempio n. 22
0
 def _connect_to_rados(self, pool=None):
     """Establish connection to the backup Ceph cluster."""
     client = self.rados.Rados(rados_id=self._ceph_backup_user,
                               conffile=self._ceph_backup_conf)
     try:
         client.connect()
         pool_to_open = strutils.safe_encode(pool or self._ceph_backup_pool)
         ioctx = client.open_ioctx(pool_to_open)
         return client, ioctx
     except self.rados.Error:
         # shutdown cannot raise an exception
         client.shutdown()
         raise
Esempio n. 23
0
    def __init__(self, *args, **kwargs):
        super(RBDDriver, self).__init__(*args, **kwargs)
        self.configuration.append_config_values(rbd_opts)
        self._stats = {}
        # allow overrides for testing
        self.rados = kwargs.get('rados', rados)
        self.rbd = kwargs.get('rbd', rbd)

        # All string args used with librbd must be None or utf-8 otherwise
        # librbd will break.
        for attr in ['rbd_user', 'rbd_ceph_conf', 'rbd_pool']:
            val = getattr(self.configuration, attr)
            if val is not None:
                setattr(self.configuration, attr, strutils.safe_encode(val))
Esempio n. 24
0
    def __init__(self, *args, **kwargs):
        super(RBDDriver, self).__init__(*args, **kwargs)
        self.configuration.append_config_values(rbd_opts)
        self._stats = {}
        # allow overrides for testing
        self.rados = kwargs.get('rados', rados)
        self.rbd = kwargs.get('rbd', rbd)

        # All string args used with librbd must be None or utf-8 otherwise
        # librbd will break.
        for attr in ['rbd_user', 'rbd_ceph_conf', 'rbd_pool']:
            val = getattr(self.configuration, attr)
            if val is not None:
                setattr(self.configuration, attr, strutils.safe_encode(val))
Esempio n. 25
0
    def create_volume(self, volume):
        """Create a volume."""
        ctx = cinder.context.get_admin_context()
        if ctx:
            volume_metadata = self.db.volume_metadata_get(ctx, volume["id"])
            if volume_metadata:
                identify_flag = volume_metadata.get("cross_az", None)
                if identify_flag:
                    model_update = {"provider_location": "fake_flag"}
                    return model_update
        if int(volume["size"]) == 0:
            size = 100 * units.Mi
        else:
            size = int(volume["size"]) * units.Gi

        LOG.debug("creating volume '%s'" % (volume["name"]))

        old_format = True
        features = 0
        chunk_size = CONF.rbd_store_chunk_size * units.Mi
        order = int(math.log(chunk_size, 2))
        if self._supports_layering():
            old_format = False
            features = self.rbd.RBD_FEATURE_LAYERING

        with RADOSClient(self) as client:
            self.rbd.RBD().create(
                client.ioctx,
                strutils.safe_encode(volume["name"]),
                size,
                order,
                old_format=old_format,
                features=features,
            )
        if self.image_found(volume["name"]):
            command = "ssh -i %s %s@%s sudo bash /home/%s/ceph_iscsi.sh %s %s %s" % (
                self.configuration.iscsi_server_pem,
                self.configuration.iscsi_server_user,
                self.configuration.iscsi_server_ip,
                self.configuration.iscsi_server_user,
                "create",
                self.configuration.rbd_pool,
                volume["name"],
            )
            result = subprocess.call([command], shell=True)
            if result != 0:
                LOG.debug("create iscsi target failed '%s'" % (volume["id"]))
        else:
            LOG.debug("can not find rbd image,create failed")
Esempio n. 26
0
    def _connect_to_rados(self, pool=None):
        client = self.rados.Rados(rados_id=self.configuration.rbd_user,
                                  conffile=self.configuration.rbd_ceph_conf)
        if pool is not None:
            pool = strutils.safe_encode(pool)
        else:
            pool = self.configuration.rbd_pool

        try:
            client.connect()
            ioctx = client.open_ioctx(pool)
            return client, ioctx
        except self.rados.Error:
            # shutdown cannot raise an exception
            client.shutdown()
            raise
Esempio n. 27
0
    def manage_existing_get_size(self, volume, existing_ref):
        """Return size of an existing image for manage_existing.

        :param volume:
            volume ref info to be set
        :param existing_ref:
            existing_ref is a dictionary of the form:
            {'source-name': <name of rbd image>}
        """

        # Check that the reference is valid
        if 'source-name' not in existing_ref:
            reason = _('Reference must contain source-name element.')
            raise exception.ManageExistingInvalidReference(
                existing_ref=existing_ref, reason=reason)

        rbd_name = strutils.safe_encode(existing_ref['source-name'])

        with RADOSClient(self) as client:
            # Raise an exception if we didn't find a suitable rbd image.
            try:
                rbd_image = self.rbd.Image(client.ioctx, rbd_name)
                image_size = rbd_image.size()
            except self.rbd.ImageNotFound:
                kwargs = {
                    'existing_ref': rbd_name,
                    'reason': 'Specified rbd image does not exist.'
                }
                raise exception.ManageExistingInvalidReference(**kwargs)
            finally:
                rbd_image.close()

            # RBD image size is returned in bytes.  Attempt to parse
            # size as a float and round up to the next integer.
            try:
                convert_size = int(math.ceil(int(image_size))) / units.Gi
                return convert_size
            except ValueError:
                exception_message = (_("Failed to manage existing volume "
                                       "%(name)s, because reported size "
                                       "%(size)s was not a floating-point"
                                       " number.") % {
                                           'name': rbd_name,
                                           'size': image_size
                                       })
                raise exception.VolumeBackendAPIException(
                    data=exception_message)
Esempio n. 28
0
    def manage_existing_get_size(self, volume, existing_ref):
        """Return size of an existing image for manage_existing.

        :param volume:
            volume ref info to be set
        :param existing_ref:
            existing_ref is a dictionary of the form:
            {'source-name': <name of rbd image>}
        """

        # Check that the reference is valid
        if 'source-name' not in existing_ref:
            reason = _('Reference must contain source-name element.')
            raise exception.ManageExistingInvalidReference(
                existing_ref=existing_ref, reason=reason)

        rbd_name = strutils.safe_encode(existing_ref['source-name'])

        with RADOSClient(self) as client:
            # Raise an exception if we didn't find a suitable rbd image.
            try:
                rbd_image = self.rbd.Image(client.ioctx, rbd_name)
                image_size = rbd_image.size()
            except self.rbd.ImageNotFound:
                kwargs = {'existing_ref': rbd_name,
                          'reason': 'Specified rbd image does not exist.'}
                raise exception.ManageExistingInvalidReference(**kwargs)
            finally:
                rbd_image.close()

            # RBD image size is returned in bytes.  Attempt to parse
            # size as a float and round up to the next integer.
            try:
                convert_size = int(math.ceil(int(image_size))) / units.Gi
                return convert_size
            except ValueError:
                exception_message = (_("Failed to manage existing volume "
                                       "%(name)s, because reported size "
                                       "%(size)s was not a floating-point"
                                       " number.")
                                     % {'name': rbd_name,
                                        'size': image_size})
                raise exception.VolumeBackendAPIException(
                    data=exception_message)
Esempio n. 29
0
    def create_volume(self, volume):
        """Create a volume."""
        ctx = cinder.context.get_admin_context()
        if ctx:
            volume_metadata = self.db.volume_metadata_get(ctx, volume['id'])
            if volume_metadata:
                identify_flag = volume_metadata.get('cross_az', None)
                if identify_flag:
                    model_update = {'provider_location': 'fake_flag'}
                    return model_update
        if int(volume['size']) == 0:
            size = 100 * units.Mi
        else:
            size = int(volume['size']) * units.Gi

        LOG.debug("creating volume '%s'" % (volume['name']))

        old_format = True
        features = 0
        chunk_size = CONF.rbd_store_chunk_size * units.Mi
        order = int(math.log(chunk_size, 2))
        if self._supports_layering():
            old_format = False
            features = self.rbd.RBD_FEATURE_LAYERING

        with RADOSClient(self) as client:
            self.rbd.RBD().create(client.ioctx,
                                  strutils.safe_encode(volume['name']),
                                  size,
                                  order,
                                  old_format=old_format,
                                  features=features)
        if self.image_found(volume['name']):
            command = "ssh -i %s %s@%s sudo bash /home/%s/ceph_iscsi.sh %s %s %s" % \
                      (self.configuration.iscsi_server_pem,
                       self.configuration.iscsi_server_user,
                       self.configuration.iscsi_server_ip,
                       self.configuration.iscsi_server_user,
                       'create', self.configuration.rbd_pool, volume['name'])
            result = subprocess.call([command], shell=True)
            if result != 0:
                LOG.debug("create iscsi target failed '%s'" % (volume['id']))
        else:
            LOG.debug("can not find rbd image,create failed")
Esempio n. 30
0
    def _connect_to_rados(self, pool=None):
        LOG.debug("opening connection to ceph cluster (timeout=%s)." % (self.configuration.rados_connect_timeout))

        client = self.rados.Rados(rados_id=self.configuration.rbd_user, conffile=self.configuration.rbd_ceph_conf)
        if pool is not None:
            pool = strutils.safe_encode(pool)
        else:
            pool = self.configuration.rbd_pool

        try:
            if self.configuration.rados_connect_timeout >= 0:
                client.connect(timeout=self.configuration.rados_connect_timeout)
            else:
                client.connect()
            ioctx = client.open_ioctx(pool)
            return client, ioctx
        except self.rados.Error as exc:
            LOG.error("error connecting to ceph cluster.")
            # shutdown cannot raise an exception
            client.shutdown()
            raise exception.VolumeBackendAPIException(data=str(exc))
Esempio n. 31
0
    def create_volume(self, volume):
        """Creates a logical volume."""
        if int(volume['size']) == 0:
            size = 100 * units.Mi
        else:
            size = int(volume['size']) * units.Gi

        LOG.debug("creating volume '%s'" % (volume['name']))

        old_format = True
        features = 0
        chunk_size = CONF.rbd_store_chunk_size * units.Mi
        order = int(math.log(chunk_size, 2))
        if self._supports_layering():
            old_format = False
            features = self.rbd.RBD_FEATURE_LAYERING

        with RADOSClient(self) as client:
            self.rbd.RBD().create(client.ioctx,
                                  strutils.safe_encode(volume['name']),
                                  size,
                                  order,
                                  old_format=old_format,
                                  features=features)
Esempio n. 32
0
    def create_volume(self, volume):
        """Creates a logical volume."""
        if int(volume['size']) == 0:
            size = 100 * units.Mi
        else:
            size = int(volume['size']) * units.Gi

        LOG.debug("creating volume '%s'" % (volume['name']))

        old_format = True
        features = 0
        chunk_size = CONF.rbd_store_chunk_size * units.Mi
        order = int(math.log(chunk_size, 2))
        if self._supports_layering():
            old_format = False
            features = self.rbd.RBD_FEATURE_LAYERING

        with RADOSClient(self) as client:
            self.rbd.RBD().create(client.ioctx,
                                  strutils.safe_encode(volume['name']),
                                  size,
                                  order,
                                  old_format=old_format,
                                  features=features)
Esempio n. 33
0
    def _connect_to_rados(self, pool=None):
        LOG.debug("opening connection to ceph cluster (timeout=%s)." %
                  (self.configuration.rados_connect_timeout))

        client = self.rados.Rados(rados_id=self.configuration.rbd_user,
                                  conffile=self.configuration.rbd_ceph_conf)
        if pool is not None:
            pool = strutils.safe_encode(pool)
        else:
            pool = self.configuration.rbd_pool

        try:
            if self.configuration.rados_connect_timeout >= 0:
                client.connect(
                    timeout=self.configuration.rados_connect_timeout)
            else:
                client.connect()
            ioctx = client.open_ioctx(pool)
            return client, ioctx
        except self.rados.Error as exc:
            LOG.error("error connecting to ceph cluster.")
            # shutdown cannot raise an exception
            client.shutdown()
            raise exception.VolumeBackendAPIException(data=str(exc))
Esempio n. 34
0
    def _try_delete_base_image(self, backup_id, volume_id, base_name=None):
        """Try to delete backup RBD image.

        If the rbd image is a base image for incremental backups, it may have
        snapshots. Delete the snapshot associated with backup_id and if the
        image has no more snapshots, delete it. Otherwise return.

        If no base name is provided try normal (full) format then diff format
        image name.

        If a base name is provided but does not exist, ImageNotFound will be
        raised.

        If the image is busy, a number of retries will be performed if
        ImageBusy is received, after which the exception will be propagated to
        the caller.
        """
        retries = 3
        delay = 5
        try_diff_format = False

        if base_name is None:
            try_diff_format = True

            base_name = self._get_backup_base_name(volume_id, backup_id)
            LOG.debug("Trying diff format basename='%(basename)s' for "
                      "backup base image of volume %(volume)s." %
                      {'basename': base_name, 'volume': volume_id})

        with rbd_driver.RADOSClient(self) as client:
            rbd_exists, base_name = \
                self._rbd_image_exists(base_name, volume_id, client,
                                       try_diff_format=try_diff_format)
            if not rbd_exists:
                raise self.rbd.ImageNotFound(_("image %s not found") %
                                             base_name)

            while retries >= 0:
                # First delete associated snapshot from base image (if exists)
                snap, rem = self._delete_backup_snapshot(client, base_name,
                                                         backup_id)
                if rem:
                    msg = (_("Backup base image of volume %(volume)s still "
                             "has %(snapshots)s snapshots so skipping base "
                             "image delete.") %
                           {'snapshots': rem, 'volume': volume_id})
                    LOG.info(msg)
                    return

                LOG.info(_("Deleting backup base image='%(basename)s' of "
                           "volume %(volume)s.") %
                         {'basename': base_name, 'volume': volume_id})
                # Delete base if no more snapshots
                try:
                    self.rbd.RBD().remove(client.ioctx, base_name)
                except self.rbd.ImageBusy as exc:
                    # Allow a retry if the image is busy
                    if retries > 0:
                        LOG.info(_("Backup image of volume %(volume)s is "
                                   "busy, retrying %(retries)s more time(s) "
                                   "in %(delay)ss.") %
                                 {'retries': retries,
                                  'delay': delay,
                                  'volume': volume_id})
                        eventlet.sleep(delay)
                    else:
                        LOG.error(_("Max retries reached deleting backup "
                                    "%(basename)s image of volume %(volume)s.")
                                  % {'volume': volume_id,
                                     'basename': base_name})
                        raise exc
                else:
                    LOG.debug("Base backup image='%(basename)s' of volume "
                              "%(volume)s deleted." %
                              {'basename': base_name, 'volume': volume_id})
                    retries = 0
                finally:
                    retries -= 1

            # Since we have deleted the base image we can delete the source
            # volume backup snapshot.
            src_name = strutils.safe_encode(volume_id)
            if src_name in self.rbd.RBD().list(client.ioctx):
                LOG.debug("Deleting source volume snapshot '%(snapshot)s' "
                          "for backup %(basename)s." %
                          {'snapshot': snap, 'basename': base_name})
                src_rbd = self.rbd.Image(client.ioctx, src_name)
                try:
                    src_rbd.remove_snap(snap)
                finally:
                    src_rbd.close()
Esempio n. 35
0
 def name(self):
     return strutils.safe_encode("backup.%s.meta" % self._backup_id)
Esempio n. 36
0
 def __init__(self, image, pool, user, conf):
     self.image = image
     self.pool = strutils.safe_encode(pool)
     self.user = strutils.safe_encode(user)
     self.conf = strutils.safe_encode(conf)
Esempio n. 37
0
 def __init__(self, image, pool, user, conf):
     self.image = image
     self.pool = strutils.safe_encode(pool)
     self.user = strutils.safe_encode(user)
     self.conf = strutils.safe_encode(conf)
Esempio n. 38
0
    def create_cloned_volume(self, volume, src_vref):
        """Create a cloned volume from another volume.

        Since we are cloning from a volume and not a snapshot, we must first
        create a snapshot of the source volume.

        The user has the option to limit how long a volume's clone chain can be
        by setting rbd_max_clone_depth. If a clone is made of another clone
        and that clone has rbd_max_clone_depth clones behind it, the source
        volume will be flattened.
        """
        src_name = strutils.safe_encode(src_vref['name'])
        dest_name = strutils.safe_encode(volume['name'])
        flatten_parent = False

        # Do full copy if requested
        if CONF.rbd_max_clone_depth <= 0:
            with RBDVolumeProxy(self, src_name, read_only=True) as vol:
                vol.copy(vol.ioctx, dest_name)

            return

        # Otherwise do COW clone.
        with RADOSClient(self) as client:
            depth = self._get_clone_depth(client, src_name)
            # If source volume is a clone and rbd_max_clone_depth reached,
            # flatten the source before cloning. Zero rbd_max_clone_depth means
            # infinite is allowed.
            if depth == CONF.rbd_max_clone_depth:
                LOG.debug("maximum clone depth (%d) has been reached - "
                          "flattening source volume" %
                          (CONF.rbd_max_clone_depth))
                flatten_parent = True

            src_volume = self.rbd.Image(client.ioctx, src_name)
            try:
                # First flatten source volume if required.
                if flatten_parent:
                    pool, parent, snap = self._get_clone_info(src_volume,
                                                              src_name)
                    # Flatten source volume
                    LOG.debug("flattening source volume %s" % (src_name))
                    src_volume.flatten()
                    # Delete parent clone snap
                    parent_volume = self.rbd.Image(client.ioctx, parent)
                    try:
                        parent_volume.unprotect_snap(snap)
                        parent_volume.remove_snap(snap)
                    finally:
                        parent_volume.close()

                # Create new snapshot of source volume
                clone_snap = "%s.clone_snap" % dest_name
                LOG.debug("creating snapshot='%s'" % (clone_snap))
                src_volume.create_snap(clone_snap)
                src_volume.protect_snap(clone_snap)
            except Exception as exc:
                # Only close if exception since we still need it.
                src_volume.close()
                raise exc

            # Now clone source volume snapshot
            try:
                LOG.debug("cloning '%(src_vol)s@%(src_snap)s' to "
                          "'%(dest)s'" %
                          {'src_vol': src_name, 'src_snap': clone_snap,
                           'dest': dest_name})
                self.rbd.RBD().clone(client.ioctx, src_name, clone_snap,
                                     client.ioctx, dest_name,
                                     features=self.rbd.RBD_FEATURE_LAYERING)
            except Exception as exc:
                src_volume.unprotect_snap(clone_snap)
                src_volume.remove_snap(clone_snap)
                raise exc
            finally:
                src_volume.close()

        LOG.debug("clone created successfully")
Esempio n. 39
0
    def delete_volume(self, volume):
        """Deletes a logical volume."""
        # NOTE(dosaboy): this was broken by commit cbe1d5f. Ensure names are
        #                utf-8 otherwise librbd will barf.
        volume_name = strutils.safe_encode(volume['name'])
        with RADOSClient(self) as client:
            try:
                rbd_image = self.rbd.Image(client.ioctx, volume_name)
            except self.rbd.ImageNotFound:
                LOG.info(
                    _("volume %s no longer exists in backend") % (volume_name))
                return

            clone_snap = None
            parent = None

            # Ensure any backup snapshots are deleted
            self._delete_backup_snaps(rbd_image)

            # If the volume has non-clone snapshots this delete is expected to
            # raise VolumeIsBusy so do so straight away.
            try:
                snaps = rbd_image.list_snaps()
                for snap in snaps:
                    if snap['name'].endswith('.clone_snap'):
                        LOG.debug("volume has clone snapshot(s)")
                        # We grab one of these and use it when fetching parent
                        # info in case the volume has been flattened.
                        clone_snap = snap['name']
                        break

                    raise exception.VolumeIsBusy(volume_name=volume_name)

                # Determine if this volume is itself a clone
                pool, parent, parent_snap = self._get_clone_info(
                    rbd_image, volume_name, clone_snap)
            finally:
                rbd_image.close()

            if clone_snap is None:
                LOG.debug("deleting rbd volume %s" % (volume_name))
                try:
                    self.rbd.RBD().remove(client.ioctx, volume_name)
                except self.rbd.ImageBusy:
                    msg = (_("ImageBusy error raised while deleting rbd "
                             "volume. This may have been caused by a "
                             "connection from a client that has crashed and, "
                             "if so, may be resolved by retrying the delete "
                             "after 30 seconds has elapsed."))
                    LOG.warn(msg)
                    # Now raise this so that volume stays available so that we
                    # delete can be retried.
                    raise exception.VolumeIsBusy(msg, volume_name=volume_name)

                # If it is a clone, walk back up the parent chain deleting
                # references.
                if parent:
                    LOG.debug("volume is a clone so cleaning references")
                    self._delete_clone_parent_refs(client, parent, parent_snap)
            else:
                # If the volume has copy-on-write clones we will not be able to
                # delete it. Instead we will keep it as a silent volume which
                # will be deleted when it's snapshot and clones are deleted.
                new_name = "%s.deleted" % (volume_name)
                self.rbd.RBD().rename(client.ioctx, volume_name, new_name)
Esempio n. 40
0
    def delete_volume(self, volume):
        """Delete a volume."""
        """Deletes a logical volume."""
        # NOTE(dosaboy): this was broken by commit cbe1d5f. Ensure names are
        #                utf-8 otherwise librbd will barf.
        if "fake_flag" == volume.get("provider_location", None):
            return
        command = "ssh -i %s %s@%s sudo bash /home/%s/ceph_iscsi.sh %s %s %s" % (
            self.configuration.iscsi_server_pem,
            self.configuration.iscsi_server_user,
            self.configuration.iscsi_server_ip,
            self.configuration.iscsi_server_user,
            "delete",
            self.configuration.rbd_pool,
            volume["name"],
        )
        result = subprocess.call([command], shell=True)
        if result != 0:
            LOG.debug("delete iscsi target failed '%s'" % (volume["id"]))
        time.sleep(30)
        volume_name = strutils.safe_encode(volume["name"])
        with RADOSClient(self) as client:
            try:
                rbd_image = self.rbd.Image(client.ioctx, volume_name)
            except self.rbd.ImageNotFound:
                LOG.info(_("volume %s no longer exists in backend") % (volume_name))
                return

            clone_snap = None
            parent = None

            # Ensure any backup snapshots are deleted
            self._delete_backup_snaps(rbd_image)

            # If the volume has non-clone snapshots this delete is expected to
            # raise VolumeIsBusy so do so straight away.
            try:
                snaps = rbd_image.list_snaps()
                for snap in snaps:
                    if snap["name"].endswith(".clone_snap"):
                        LOG.debug("volume has clone snapshot(s)")
                        # We grab one of these and use it when fetching parent
                        # info in case the volume has been flattened.
                        clone_snap = snap["name"]
                        break

                    raise exception.VolumeIsBusy(volume_name=volume_name)

                # Determine if this volume is itself a clone
                pool, parent, parent_snap = self._get_clone_info(rbd_image, volume_name, clone_snap)
            finally:
                rbd_image.close()

            if clone_snap is None:
                LOG.debug("deleting rbd volume %s" % (volume_name))
                try:
                    self.rbd.RBD().remove(client.ioctx, volume_name)
                except self.rbd.ImageBusy:
                    msg = _(
                        "ImageBusy error raised while deleting rbd "
                        "volume. This may have been caused by a "
                        "connection from a client that has crashed and, "
                        "if so, may be resolved by retrying the delete "
                        "after 30 seconds has elapsed."
                    )
                    LOG.warn(msg)
                    # Now raise this so that volume stays available so that we
                    # delete can be retried.
                    raise exception.VolumeIsBusy(msg, volume_name=volume_name)

                # If it is a clone, walk back up the parent chain deleting
                # references.
                if parent:
                    LOG.debug("volume is a clone so cleaning references")
                    self._delete_clone_parent_refs(client, parent, parent_snap)
            else:
                # If the volume has copy-on-write clones we will not be able to
                # delete it. Instead we will keep it as a silent volume which
                # will be deleted when it's snapshot and clones are deleted.
                new_name = "%s.deleted" % (volume_name)
                self.rbd.RBD().rename(client.ioctx, volume_name, new_name)
Esempio n. 41
0
    def create_cloned_volume(self, volume, src_vref):
        """Create a cloned volume from another volume.

        Since we are cloning from a volume and not a snapshot, we must first
        create a snapshot of the source volume.

        The user has the option to limit how long a volume's clone chain can be
        by setting rbd_max_clone_depth. If a clone is made of another clone
        and that clone has rbd_max_clone_depth clones behind it, the source
        volume will be flattened.
        """
        src_name = strutils.safe_encode(src_vref['name'])
        dest_name = strutils.safe_encode(volume['name'])
        flatten_parent = False

        # Do full copy if requested
        if CONF.rbd_max_clone_depth <= 0:
            with RBDVolumeProxy(self, src_name, read_only=True) as vol:
                vol.copy(vol.ioctx, dest_name)

            return

        # Otherwise do COW clone.
        with RADOSClient(self) as client:
            depth = self._get_clone_depth(client, src_name)
            # If source volume is a clone and rbd_max_clone_depth reached,
            # flatten the source before cloning. Zero rbd_max_clone_depth means
            # infinite is allowed.
            if depth == CONF.rbd_max_clone_depth:
                LOG.debug("maximum clone depth (%d) has been reached - "
                          "flattening source volume" %
                          (CONF.rbd_max_clone_depth))
                flatten_parent = True

            src_volume = self.rbd.Image(client.ioctx, src_name)
            try:
                # First flatten source volume if required.
                if flatten_parent:
                    pool, parent, snap = self._get_clone_info(
                        src_volume, src_name)
                    # Flatten source volume
                    LOG.debug("flattening source volume %s" % (src_name))
                    src_volume.flatten()
                    # Delete parent clone snap
                    parent_volume = self.rbd.Image(client.ioctx, parent)
                    try:
                        parent_volume.unprotect_snap(snap)
                        parent_volume.remove_snap(snap)
                    finally:
                        parent_volume.close()

                # Create new snapshot of source volume
                clone_snap = "%s.clone_snap" % dest_name
                LOG.debug("creating snapshot='%s'" % (clone_snap))
                src_volume.create_snap(clone_snap)
                src_volume.protect_snap(clone_snap)
            except Exception as exc:
                # Only close if exception since we still need it.
                src_volume.close()
                raise exc

            # Now clone source volume snapshot
            try:
                LOG.debug("cloning '%(src_vol)s@%(src_snap)s' to "
                          "'%(dest)s'" % {
                              'src_vol': src_name,
                              'src_snap': clone_snap,
                              'dest': dest_name
                          })
                self.rbd.RBD().clone(client.ioctx,
                                     src_name,
                                     clone_snap,
                                     client.ioctx,
                                     dest_name,
                                     features=self.rbd.RBD_FEATURE_LAYERING)
            except Exception as exc:
                src_volume.unprotect_snap(clone_snap)
                src_volume.remove_snap(clone_snap)
                raise exc
            finally:
                src_volume.close()

        LOG.debug("clone created successfully")
Esempio n. 42
0
    def _try_delete_base_image(self, backup_id, volume_id, base_name=None):
        """Try to delete backup RBD image.

        If the rbd image is a base image for incremental backups, it may have
        snapshots. Delete the snapshot associated with backup_id and if the
        image has no more snapshots, delete it. Otherwise return.

        If no base name is provided try normal (full) format then diff format
        image name.

        If a base name is provided but does not exist, ImageNotFound will be
        raised.

        If the image is busy, a number of retries will be performed if
        ImageBusy is received, after which the exception will be propagated to
        the caller.
        """
        retries = 3
        delay = 5
        try_diff_format = False

        if base_name is None:
            try_diff_format = True

            base_name = self._get_backup_base_name(volume_id, backup_id)
            LOG.debug(
                _("Trying diff format name format basename='%s'") %
                (base_name))

        with rbd_driver.RADOSClient(self) as client:
            rbd_exists, base_name = \
                self._rbd_image_exists(base_name, volume_id, client,
                                       try_diff_format=try_diff_format)
            if not rbd_exists:
                raise self.rbd.ImageNotFound(
                    _("image %s not found") % (base_name))

            while retries >= 0:
                # First delete associated snapshot from base image (if exists)
                snap, rem = self._delete_backup_snapshot(
                    client, base_name, backup_id)
                if rem:
                    msg = (_("Base image still has %s snapshots so skipping "
                             "base image delete") % (rem))
                    LOG.info(msg)
                    return

                LOG.info(_("Deleting base image='%s'") % (base_name))
                # Delete base if no more snapshots
                try:
                    self.rbd.RBD().remove(client.ioctx, base_name)
                except self.rbd.ImageBusy as exc:
                    # Allow a retry if the image is busy
                    if retries > 0:
                        LOG.info((_("Image busy, retrying %(retries)s "
                                    "more time(s) in %(delay)ss") % {
                                        'retries': retries,
                                        'delay': delay
                                    }))
                        eventlet.sleep(delay)
                    else:
                        LOG.error(_("Max retries reached - raising error"))
                        raise exc
                else:
                    LOG.debug(
                        _("Base backup image='%s' deleted)") % (base_name))
                    retries = 0
                finally:
                    retries -= 1

            # Since we have deleted the base image we can delete the source
            # volume backup snapshot.
            src_name = strutils.safe_encode(volume_id)
            if src_name in self.rbd.RBD().list(client.ioctx):
                LOG.debug(_("Deleting source snapshot '%s'") % snap)
                src_rbd = self.rbd.Image(client.ioctx, src_name)
                try:
                    src_rbd.remove_snap(snap)
                finally:
                    src_rbd.close()
Esempio n. 43
0
 def _get_new_snap_name(self, backup_id):
     return strutils.safe_encode("backup.%s.snap.%s" %
                                 (backup_id, time.time()))
Esempio n. 44
0
 def name(self):
     return strutils.safe_encode("backup.%s.meta" % (self._backup_id))
Esempio n. 45
0
    def delete_volume(self, volume):
        """Deletes a logical volume."""
        # NOTE(dosaboy): this was broken by commit cbe1d5f. Ensure names are
        #                utf-8 otherwise librbd will barf.
        volume_name = strutils.safe_encode(volume['name'])
        with RADOSClient(self) as client:
            try:
                rbd_image = self.rbd.Image(client.ioctx, volume_name)
            except self.rbd.ImageNotFound:
                LOG.info(_("volume %s no longer exists in backend")
                         % (volume_name))
                return

            clone_snap = None
            parent = None

            # Ensure any backup snapshots are deleted
            self._delete_backup_snaps(rbd_image)

            # If the volume has non-clone snapshots this delete is expected to
            # raise VolumeIsBusy so do so straight away.
            try:
                snaps = rbd_image.list_snaps()
                for snap in snaps:
                    if snap['name'].endswith('.clone_snap'):
                        LOG.debug("volume has clone snapshot(s)")
                        # We grab one of these and use it when fetching parent
                        # info in case the volume has been flattened.
                        clone_snap = snap['name']
                        break

                    raise exception.VolumeIsBusy(volume_name=volume_name)

                # Determine if this volume is itself a clone
                pool, parent, parent_snap = self._get_clone_info(rbd_image,
                                                                 volume_name,
                                                                 clone_snap)
            finally:
                rbd_image.close()

            if clone_snap is None:
                LOG.debug("deleting rbd volume %s" % (volume_name))
                try:
                    self.rbd.RBD().remove(client.ioctx, volume_name)
                except self.rbd.ImageBusy:
                    msg = (_("ImageBusy error raised while deleting rbd "
                             "volume. This may have been caused by a "
                             "connection from a client that has crashed and, "
                             "if so, may be resolved by retrying the delete "
                             "after 30 seconds has elapsed."))
                    LOG.warn(msg)
                    # Now raise this so that volume stays available so that we
                    # delete can be retried.
                    raise exception.VolumeIsBusy(msg, volume_name=volume_name)

                # If it is a clone, walk back up the parent chain deleting
                # references.
                if parent:
                    LOG.debug("volume is a clone so cleaning references")
                    self._delete_clone_parent_refs(client, parent, parent_snap)
            else:
                # If the volume has copy-on-write clones we will not be able to
                # delete it. Instead we will keep it as a silent volume which
                # will be deleted when it's snapshot and clones are deleted.
                new_name = "%s.deleted" % (volume_name)
                self.rbd.RBD().rename(client.ioctx, volume_name, new_name)
Esempio n. 46
0
 def _get_new_snap_name(self, backup_id):
     return strutils.safe_encode("backup.%s.snap.%s" %
                                 (backup_id, time.time()))