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
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
def test_share_limit_exceeded(self): # verify response code for exception.ShareLimitExceeded allowed = 776 # amount of allowed shares e = exception.ShareLimitExceeded(allowed=allowed) self.assertEqual(413, e.code) self.assertIn(str(allowed), e.msg)
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