Example #1
0
    def delete_snapshot(self, context, snapshot, force=False):
        if not (force or snapshot['status'] in ["available", "error"]):
            msg = _("Share Snapshot status must be 'available' or 'error'.")
            raise exception.InvalidShareSnapshot(reason=msg)

        self.db.share_snapshot_update(context, snapshot['id'],
                                      {'status': 'deleting'})
        share = self.db.share_get(context, snapshot['share_id'])
        self.share_rpcapi.delete_snapshot(context, snapshot, share['host'])
Example #2
0
    def delete_snapshot(self, context, snapshot, force=False):
        statuses = (constants.STATUS_AVAILABLE, constants.STATUS_ERROR)
        if not (force or snapshot['status'] in statuses):
            msg = _("Share Snapshot status must be one of %(statuses)s.") % {
                "statuses": statuses
            }
            raise exception.InvalidShareSnapshot(reason=msg)

        self.db.share_snapshot_update(context, snapshot['id'],
                                      {'status': constants.STATUS_DELETING})
        share = self.db.share_get(context, snapshot['share_id'])
        self.share_rpcapi.delete_snapshot(context, snapshot, share['host'])
Example #3
0
    def _check_snapshot_id_exist(self, snapshot_info):
        """Check the snapshot id exists."""

        if snapshot_info['error']['code'] == constants.MSG_SNAPSHOT_NOT_FOUND:
            return False
        elif snapshot_info['error']['code'] == 0:
            return True
        else:
            err_str = "Check the snapshot id exists error!"
            err_msg = (_('%(err)s\nresult: %(res)s.') % {
                'err': err_str,
                'res': snapshot_info
            })
            raise exception.InvalidShareSnapshot(reason=err_msg)
Example #4
0
    def create(self,
               context,
               share_proto,
               size,
               name,
               description,
               snapshot=None,
               availability_zone=None,
               metadata=None,
               share_network_id=None,
               volume_type=None):
        """Create new share."""
        policy.check_policy(context, 'share', 'create')

        self._check_metadata_properties(context, metadata)

        if snapshot is not None:
            if snapshot['status'] != 'available':
                msg = _('status must be available')
                raise exception.InvalidShareSnapshot(reason=msg)
            if not size:
                size = snapshot['size']

            snapshot_id = snapshot['id']
        else:
            snapshot_id = None

        def as_int(s):
            try:
                return int(s)
            except (ValueError, TypeError):
                return s

        # tolerate size as stringified int
        size = as_int(size)

        if not isinstance(size, int) or size <= 0:
            msg = (_("Share size '%s' must be an integer and greater than 0") %
                   size)
            raise exception.InvalidInput(reason=msg)

        if snapshot and size < snapshot['size']:
            msg = (_("Share size '%s' must be equal or greater "
                     "than snapshot size") % size)
            raise exception.InvalidInput(reason=msg)

        if snapshot and volume_type:
            if volume_type['id'] != snapshot['volume_type_id']:
                msg = _("Invalid volume_type provided (requested type "
                        "must match source snapshot, or be omitted). "
                        "You should omit the argument.")
                raise exception.InvalidInput(reason=msg)

        # TODO(rushiagr): Find a suitable place to keep all the allowed
        #                 share types so that it becomes easier to add one
        if share_proto.lower() not in ['nfs', 'cifs']:
            msg = (_("Invalid share type provided: %s") % share_proto)
            raise exception.InvalidInput(reason=msg)

        try:
            reservations = QUOTAS.reserve(context, shares=1, gigabytes=size)
        except exception.OverQuota as e:
            overs = e.kwargs['overs']
            usages = e.kwargs['usages']
            quotas = e.kwargs['quotas']

            def _consumed(name):
                return (usages[name]['reserved'] + usages[name]['in_use'])

            if 'gigabytes' in overs:
                msg = _("Quota exceeded for %(s_pid)s, tried to create "
                        "%(s_size)sG share (%(d_consumed)dG of %(d_quota)dG "
                        "already consumed)")
                LOG.warn(
                    msg % {
                        's_pid': context.project_id,
                        's_size': size,
                        'd_consumed': _consumed('gigabytes'),
                        'd_quota': quotas['gigabytes']
                    })
                raise exception.ShareSizeExceedsAvailableQuota()
            elif 'shares' in overs:
                msg = _("Quota exceeded for %(s_pid)s, tried to create "
                        "share (%(d_consumed)d shares "
                        "already consumed)")
                LOG.warn(msg % {
                    's_pid': context.project_id,
                    'd_consumed': _consumed('shares')
                })
                raise exception.ShareLimitExceeded(allowed=quotas['shares'])

        if availability_zone is None:
            availability_zone = CONF.storage_availability_zone

        options = {
            'size': size,
            'user_id': context.user_id,
            'project_id': context.project_id,
            'snapshot_id': snapshot_id,
            'share_network_id': share_network_id,
            'availability_zone': availability_zone,
            'metadata': metadata,
            'status': "creating",
            'scheduled_at': timeutils.utcnow(),
            'display_name': name,
            'display_description': description,
            'share_proto': share_proto,
            'volume_type_id': volume_type['id'] if volume_type else None
        }

        try:
            share = self.db.share_create(context, options)
            QUOTAS.commit(context, reservations)
        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    self.db.share_delete(context, share['id'])
                finally:
                    QUOTAS.rollback(context, reservations)

        request_spec = {
            'share_properties': options,
            'share_proto': share_proto,
            'share_id': share['id'],
            'snapshot_id': share['snapshot_id'],
            'volume_type': volume_type
        }

        filter_properties = {}

        self.scheduler_rpcapi.create_share(context,
                                           CONF.share_topic,
                                           share['id'],
                                           snapshot_id,
                                           request_spec=request_spec,
                                           filter_properties=filter_properties)

        return share
Example #5
0
 def test_invalid_share_snapshot(self):
     # Verify response code for exception.InvalidShareSnapshot
     reason = "fake_reason"
     e = exception.InvalidShareSnapshot(reason=reason)
     self.assertEqual(400, e.code)
     self.assertIn(reason, e.msg)
Example #6
0
    def create(self,
               context,
               share_proto,
               size,
               name,
               description,
               snapshot=None,
               availability_zone=None,
               metadata=None,
               share_network_id=None,
               share_type=None,
               is_public=False,
               consistency_group_id=None,
               cgsnapshot_member=None):
        """Create new share."""
        policy.check_policy(context, 'share', 'create')

        self._check_metadata_properties(context, metadata)

        if snapshot is not None:
            if snapshot['status'] != constants.STATUS_AVAILABLE:
                msg = _("status must be '%s'") % constants.STATUS_AVAILABLE
                raise exception.InvalidShareSnapshot(reason=msg)
            if not size:
                size = snapshot['size']

            snapshot_id = snapshot['id']
        else:
            snapshot_id = None

        def as_int(s):
            try:
                return int(s)
            except (ValueError, TypeError):
                return s

        # tolerate size as stringified int
        size = as_int(size)

        if not isinstance(size, int) or size <= 0:
            msg = (_("Share size '%s' must be an integer and greater than 0") %
                   size)
            raise exception.InvalidInput(reason=msg)

        if snapshot and size < snapshot['size']:
            msg = (_("Share size '%s' must be equal or greater "
                     "than snapshot size") % size)
            raise exception.InvalidInput(reason=msg)

        if snapshot is None:
            share_type_id = share_type['id'] if share_type else None
        else:
            source_share = self.db.share_get(context, snapshot['share_id'])
            availability_zone = source_share['availability_zone']
            if share_type is None:
                share_type_id = source_share['share_type_id']
            else:
                share_type_id = share_type['id']
                if share_type_id != source_share['share_type_id']:
                    msg = _("Invalid share type specified: the requested "
                            "share type must match the type of the source "
                            "share. If a share type is not specified when "
                            "requesting a new share from a snapshot, the "
                            "share type of the source share will be applied "
                            "to the new share.")
                    raise exception.InvalidInput(reason=msg)

        supported_share_protocols = (proto.upper()
                                     for proto in CONF.enabled_share_protocols)
        if not (share_proto
                and share_proto.upper() in supported_share_protocols):
            msg = (_("Invalid share protocol provided: %(provided)s. "
                     "It is either disabled or unsupported. Available "
                     "protocols: %(supported)s") %
                   dict(provided=share_proto,
                        supported=CONF.enabled_share_protocols))
            raise exception.InvalidInput(reason=msg)

        try:
            reservations = QUOTAS.reserve(context, shares=1, gigabytes=size)
        except exception.OverQuota as e:
            overs = e.kwargs['overs']
            usages = e.kwargs['usages']
            quotas = e.kwargs['quotas']

            def _consumed(name):
                return (usages[name]['reserved'] + usages[name]['in_use'])

            if 'gigabytes' in overs:
                LOG.warn(
                    _LW("Quota exceeded for %(s_pid)s, tried to create "
                        "%(s_size)sG share (%(d_consumed)dG of "
                        "%(d_quota)dG already consumed)"), {
                            's_pid': context.project_id,
                            's_size': size,
                            'd_consumed': _consumed('gigabytes'),
                            'd_quota': quotas['gigabytes']
                        })
                raise exception.ShareSizeExceedsAvailableQuota()
            elif 'shares' in overs:
                LOG.warn(
                    _LW("Quota exceeded for %(s_pid)s, tried to create "
                        "share (%(d_consumed)d shares "
                        "already consumed)"), {
                            's_pid': context.project_id,
                            'd_consumed': _consumed('shares')
                        })
                raise exception.ShareLimitExceeded(allowed=quotas['shares'])

        try:
            is_public = strutils.bool_from_string(is_public, strict=True)
            snapshot_support = strutils.bool_from_string(
                share_type.get('extra_specs', {}).get('snapshot_support', True)
                if share_type else True,
                strict=True)
        except ValueError as e:
            raise exception.InvalidParameterValue(six.text_type(e))

        consistency_group = None
        if consistency_group_id:
            try:
                consistency_group = self.db.consistency_group_get(
                    context, consistency_group_id)
            except exception.NotFound as e:
                raise exception.InvalidParameterValue(six.text_type(e))

            if (not cgsnapshot_member and not (consistency_group['status']
                                               == constants.STATUS_AVAILABLE)):
                params = {
                    'avail': constants.STATUS_AVAILABLE,
                    'cg_status': consistency_group['status'],
                }
                msg = _("Consistency group status must be %(avail)s, got"
                        "%(cg_status)s.") % params
                raise exception.InvalidConsistencyGroup(message=msg)

            if share_type_id:
                cg_st_ids = [
                    st['share_type_id']
                    for st in consistency_group.get('share_types', [])
                ]
                if share_type_id not in cg_st_ids:
                    params = {
                        'type': share_type_id,
                        'cg': consistency_group_id
                    }
                    msg = _("The specified share type (%(type)s) is not "
                            "supported by the specified consistency group "
                            "(%(cg)s).") % params
                    raise exception.InvalidParameterValue(msg)

            if (not consistency_group.get('share_network_id')
                    == share_network_id):
                params = {'net': share_network_id, 'cg': consistency_group_id}
                msg = _("The specified share network (%(net)s) is not "
                        "supported by the specified consistency group "
                        "(%(cg)s).") % params
                raise exception.InvalidParameterValue(msg)

        options = {
            'size': size,
            'user_id': context.user_id,
            'project_id': context.project_id,
            'snapshot_id': snapshot_id,
            'snapshot_support': snapshot_support,
            'metadata': metadata,
            'display_name': name,
            'display_description': description,
            'share_proto': share_proto,
            'share_type_id': share_type_id,
            'is_public': is_public,
            'consistency_group_id': consistency_group_id,
        }
        if cgsnapshot_member:
            options['source_cgsnapshot_member_id'] = cgsnapshot_member['id']

        try:
            share = self.db.share_create(context,
                                         options,
                                         create_share_instance=False)
            QUOTAS.commit(context, reservations)
        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    self.db.share_delete(context, share['id'])
                    ownername = context.user_id
#lease.delete_success(share['id'], ownername)
                finally:
                    QUOTAS.rollback(context, reservations)

        host = None
        if snapshot and not CONF.use_scheduler_creating_share_from_snapshot:
            # Shares from snapshots with restriction - source host only.
            # It is common situation for different types of backends.
            host = snapshot['share']['host']

        self.create_instance(context,
                             share,
                             share_network_id=share_network_id,
                             host=host,
                             availability_zone=availability_zone,
                             consistency_group=consistency_group,
                             cgsnapshot_member=cgsnapshot_member)

        # Retrieve the share with instance details
        share = self.db.share_get(context, share['id'])
        ownername = context.user_id
        lease.create_success(ownername, share['id'], name, size)

        return share
Example #7
0
    def create(self,
               context,
               share_proto,
               size,
               name,
               description,
               snapshot=None,
               availability_zone=None,
               metadata=None,
               share_network_id=None,
               share_type=None,
               is_public=False):
        """Create new share."""
        policy.check_policy(context, 'share', 'create')

        self._check_metadata_properties(context, metadata)

        if snapshot is not None:
            if snapshot['status'] != 'available':
                msg = _("status must be 'available'")
                raise exception.InvalidShareSnapshot(reason=msg)
            if not size:
                size = snapshot['size']

            snapshot_id = snapshot['id']
        else:
            snapshot_id = None

        def as_int(s):
            try:
                return int(s)
            except (ValueError, TypeError):
                return s

        # tolerate size as stringified int
        size = as_int(size)

        if not isinstance(size, int) or size <= 0:
            msg = (_("Share size '%s' must be an integer and greater than 0") %
                   size)
            raise exception.InvalidInput(reason=msg)

        if snapshot and size < snapshot['size']:
            msg = (_("Share size '%s' must be equal or greater "
                     "than snapshot size") % size)
            raise exception.InvalidInput(reason=msg)

        if snapshot is None:
            share_type_id = share_type['id'] if share_type else None
        else:
            source_share = self.db.share_get(context, snapshot['share_id'])
            if share_type is None:
                share_type_id = source_share['share_type_id']
                if share_type_id is not None:
                    share_type = share_types.get_share_type(
                        context, share_type_id)
            else:
                share_type_id = share_type['id']
                if share_type_id != source_share['share_type_id']:
                    msg = _("Invalid share type specified: the requested "
                            "share type must match the type of the source "
                            "share. If a share type is not specified when "
                            "requesting a new share from a snapshot, the "
                            "share type of the source share will be applied "
                            "to the new share.")
                    raise exception.InvalidInput(reason=msg)

        supported_share_protocols = (proto.upper()
                                     for proto in CONF.enabled_share_protocols)
        if not (share_proto
                and share_proto.upper() in supported_share_protocols):
            msg = (_("Invalid share protocol provided: %(provided)s. "
                     "It is either disabled or unsupported. Available "
                     "protocols: %(supported)s") %
                   dict(provided=share_proto,
                        supported=CONF.enabled_share_protocols))
            raise exception.InvalidInput(reason=msg)

        try:
            reservations = QUOTAS.reserve(context, shares=1, gigabytes=size)
        except exception.OverQuota as e:
            overs = e.kwargs['overs']
            usages = e.kwargs['usages']
            quotas = e.kwargs['quotas']

            def _consumed(name):
                return (usages[name]['reserved'] + usages[name]['in_use'])

            if 'gigabytes' in overs:
                LOG.warn(
                    _LW("Quota exceeded for %(s_pid)s, tried to create "
                        "%(s_size)sG share (%(d_consumed)dG of "
                        "%(d_quota)dG already consumed)"), {
                            's_pid': context.project_id,
                            's_size': size,
                            'd_consumed': _consumed('gigabytes'),
                            'd_quota': quotas['gigabytes']
                        })
                raise exception.ShareSizeExceedsAvailableQuota()
            elif 'shares' in overs:
                LOG.warn(
                    _LW("Quota exceeded for %(s_pid)s, tried to create "
                        "share (%(d_consumed)d shares "
                        "already consumed)"), {
                            's_pid': context.project_id,
                            'd_consumed': _consumed('shares')
                        })
                raise exception.ShareLimitExceeded(allowed=quotas['shares'])

        if availability_zone is None:
            availability_zone = CONF.storage_availability_zone

        try:
            is_public = strutils.bool_from_string(is_public, strict=True)
        except ValueError as e:
            raise exception.InvalidParameterValue(e.message)

        options = {
            'size': size,
            'user_id': context.user_id,
            'project_id': context.project_id,
            'snapshot_id': snapshot_id,
            'share_network_id': share_network_id,
            'availability_zone': availability_zone,
            'metadata': metadata,
            'status': "creating",
            'scheduled_at': timeutils.utcnow(),
            'display_name': name,
            'display_description': description,
            'share_proto': share_proto,
            'share_type_id': share_type_id,
            'is_public': is_public,
        }

        try:
            share = self.db.share_create(context, options)
            QUOTAS.commit(context, reservations)
        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    self.db.share_delete(context, share['id'])
                finally:
                    QUOTAS.rollback(context, reservations)

        request_spec = {
            'share_properties': options,
            'share_proto': share_proto,
            'share_id': share['id'],
            'snapshot_id': snapshot_id,
            'share_type': share_type,
        }
        filter_properties = {}

        if (snapshot and not CONF.use_scheduler_creating_share_from_snapshot):
            # Shares from snapshots with restriction - source host only.
            # It is common situation for different types of backends.
            host = snapshot['share']['host']
            share = self.db.share_update(context, share['id'], {'host': host})
            self.share_rpcapi.create_share(
                context,
                share,
                host,
                request_spec=request_spec,
                filter_properties=filter_properties,
                snapshot_id=snapshot_id,
            )
        else:
            # Shares from scratch and from snapshots when source host is not
            # the only allowed, it is possible, for example, in multibackend
            # installation with Generic drivers only.
            self.scheduler_rpcapi.create_share(
                context,
                CONF.share_topic,
                share['id'],
                snapshot_id,
                request_spec=request_spec,
                filter_properties=filter_properties,
            )

        return share