Пример #1
0
    def delete(self, context, cg):
        """Delete consistency group."""

        cg_id = cg['id']
        if not cg['host']:
            self.db.consistency_group_destroy(context.elevated(), cg_id)
            return

        statuses = (constants.STATUS_AVAILABLE, constants.STATUS_ERROR)
        if not cg['status'] in statuses:
            msg = (_("Consistency group status must be one of %(statuses)s") %
                   {
                       "statuses": statuses
                   })
            raise exception.InvalidConsistencyGroup(reason=msg)

        # NOTE(ameade): check for cgsnapshots in the CG
        if self.db.count_cgsnapshots_in_consistency_group(context, cg_id):
            msg = (_("Cannot delete a consistency group with cgsnapshots"))
            raise exception.InvalidConsistencyGroup(reason=msg)

        # NOTE(ameade): check for shares in the CG
        if self.db.count_shares_in_consistency_group(context, cg_id):
            msg = (_("Cannot delete a consistency group with shares"))
            raise exception.InvalidConsistencyGroup(reason=msg)

        cg = self.db.consistency_group_update(
            context, cg_id, {'status': constants.STATUS_DELETING})

        self.share_rpcapi.delete_consistency_group(context, cg)
Пример #2
0
    def _collate_cg_snapshot_info(self, cg_dict, cgsnapshot_dict):
        """Collate the data for a clone of a CG snapshot.

        Given two data structures, a CG snapshot (cgsnapshot_dict) and a new
        CG to be cloned from the snapshot (cg_dict), match up both structures
        into a list of dicts (share & snapshot) suitable for use by existing
        driver methods that clone individual share snapshots.
        """

        clone_list = list()

        for share in cg_dict['shares']:

            clone_info = {'share': share}

            for cgsnapshot_member in cgsnapshot_dict['cgsnapshot_members']:
                if (share['source_cgsnapshot_member_id'] ==
                        cgsnapshot_member['id']):
                    clone_info['snapshot'] = {
                        'share_id': cgsnapshot_member['share_id'],
                        'id': cgsnapshot_member['cgsnapshot_id']
                    }
                    break

            else:
                msg = _("Invalid data supplied for creating consistency group "
                        "from CG snapshot %s.") % cgsnapshot_dict['id']
                raise exception.InvalidConsistencyGroup(reason=msg)

            clone_list.append(clone_info)

        return clone_list
Пример #3
0
    def test_create_invalid_cg(self):
        fake_id = six.text_type(uuid.uuid4())
        self.mock_object(
            self.controller.cg_api, 'create_cgsnapshot',
            mock.Mock(side_effect=exception.InvalidConsistencyGroup(
                reason='bad_status')))

        body = {"cgsnapshot": {"consistency_group_id": fake_id}}
        self.assertRaises(webob.exc.HTTPConflict, self.controller.create,
                          self.request, body)
Пример #4
0
    def test_cg_delete_in_conflicting_status(self):
        fake_cg, expected_cg = self._get_fake_cg()
        self.mock_object(cg_api.API, 'get', mock.Mock(return_value=fake_cg))
        self.mock_object(
            cg_api.API, 'delete',
            mock.Mock(side_effect=exception.InvalidConsistencyGroup(
                reason='blah')))

        self.assertRaises(webob.exc.HTTPConflict, self.controller.delete,
                          self.request, fake_cg['id'])
Пример #5
0
    def test_create_invalid_cg(self):
        fake_id = six.text_type(uuidutils.generate_uuid())
        self.mock_object(
            self.controller.cg_api, 'create_cgsnapshot',
            mock.Mock(side_effect=exception.InvalidConsistencyGroup(
                reason='bad_status')))

        body = {"cgsnapshot": {"consistency_group_id": fake_id}}
        self.assertRaises(webob.exc.HTTPConflict, self.controller.create,
                          self.request, body)
        self.mock_policy_check.assert_called_once_with(self.context,
                                                       self.resource_name,
                                                       'create')
Пример #6
0
    def create_cgsnapshot(self,
                          context,
                          name=None,
                          description=None,
                          consistency_group_id=None):
        """Create new cgsnapshot."""

        options = {
            'consistency_group_id': consistency_group_id,
            'name': name,
            'description': description,
            'user_id': context.user_id,
            'project_id': context.project_id,
            'status': constants.STATUS_CREATING,
        }

        cg = self.db.consistency_group_get(context, consistency_group_id)
        # Check status of CG, must be active
        if not cg['status'] == constants.STATUS_AVAILABLE:
            msg = (_("Consistency group status must be %s") %
                   constants.STATUS_AVAILABLE)
            raise exception.InvalidConsistencyGroup(reason=msg)

        # Create members for every share in the CG
        shares = self.db.share_get_all_by_consistency_group_id(
            context, consistency_group_id)

        # Check status of all shares, they must be active in order to snap
        # the CG
        for s in shares:
            if not s['status'] == constants.STATUS_AVAILABLE:
                msg = (_("Share %(s)s in consistency group must have status "
                         "of %(status)s in order to create a CG snapshot") % {
                             "s": s['id'],
                             "status": constants.STATUS_AVAILABLE
                         })
                raise exception.InvalidConsistencyGroup(reason=msg)

        snap = self.db.cgsnapshot_create(context, options)

        try:
            members = []
            for s in shares:
                member_options = {
                    'cgsnapshot_id': snap['id'],
                    'user_id': context.user_id,
                    'project_id': context.project_id,
                    'status': constants.STATUS_CREATING,
                    'size': s['size'],
                    'share_proto': s['share_proto'],
                    'share_type_id': s['share_type_id'],
                    'share_id': s['id'],
                    'share_instance_id': s.instance['id']
                }
                member = self.db.cgsnapshot_member_create(
                    context, member_options)
                members.append(member)

            # Cast to share manager
            self.share_rpcapi.create_cgsnapshot(context, snap, cg['host'])
        except Exception:
            with excutils.save_and_reraise_exception():
                # This will delete the snapshot and all of it's members
                self.db.cgsnapshot_destroy(context, snap['id'])

        return snap
Пример #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,
               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