Exemple #1
0
    def create(self, context, name, description,
               cg_volume_types=None, availability_zone=None):

        check_policy(context, 'create')
        volume_type_list = None
        if cg_volume_types:
            volume_type_list = cg_volume_types.split(',')

        req_volume_types = []
        if volume_type_list:
            req_volume_types = (self.db.volume_types_get_by_name_or_id(
                context, volume_type_list))

        if not req_volume_types:
            volume_type = volume_types.get_default_volume_type()
            req_volume_types.append(volume_type)

        req_volume_type_ids = ""
        for voltype in req_volume_types:
            if voltype:
                req_volume_type_ids = (
                    req_volume_type_ids + voltype.get('id') + ",")
        if len(req_volume_type_ids) == 0:
            req_volume_type_ids = None

        availability_zone = self._extract_availability_zone(availability_zone)

        options = {'user_id': context.user_id,
                   'project_id': context.project_id,
                   'availability_zone': availability_zone,
                   'status': "creating",
                   'name': name,
                   'description': description,
                   'volume_type_id': req_volume_type_ids}

        group = None
        try:
            group = self.db.consistencygroup_create(context, options)
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.error(_("Error occurred when creating consistency group"
                            " %s."), name)

        request_spec_list = []
        filter_properties_list = []
        for req_volume_type in req_volume_types:
            request_spec = {'volume_type': req_volume_type.copy(),
                            'consistencygroup_id': group['id']}
            filter_properties = {}
            request_spec_list.append(request_spec)
            filter_properties_list.append(filter_properties)

        # Update quota for consistencygroups
        self.update_quota(context, group['id'])

        self._cast_create_consistencygroup(context, group['id'],
                                           request_spec_list,
                                           filter_properties_list)

        return group
Exemple #2
0
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata,
                key_manager, backup_source_volume):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        size = self._extract_size(size, source_volume, snapshot)

        self._check_image_metadata(context, image_id, size)

        availability_zone = self._extract_availability_zone(availability_zone,
                                                            snapshot,
                                                            source_volume)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoid any dependency upon the encrypted volume type.
        if not volume_type and not source_volume and not snapshot:
            volume_type = volume_types.get_default_volume_type()

        volume_type_id = self._get_volume_type_id(volume_type,
                                                  source_volume, snapshot,
                                                  backup_source_volume)

        encryption_key_id = self._get_encryption_key_id(key_manager,
                                                        context,
                                                        volume_type_id,
                                                        snapshot,
                                                        source_volume,
                                                        backup_source_volume)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            specs = qos_specs['qos_specs']
        if not specs:
            # to make sure we don't pass empty dict
            specs = None

        self._check_metadata_properties(metadata)

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'availability_zone': availability_zone,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
        }
 def test_default_volume_type_missing_in_db(self):
     """Ensures proper exception raised if default volume type
     is not in database.
     """
     session = db_api.get_session()
     default_vol_type = volume_types.get_default_volume_type()
     self.assertEqual(default_vol_type, {})
 def test_get_default_volume_type(self):
     """ Ensures default volume type can be retrieved """
     volume_types.create(self.ctxt,
                         fake_flags.def_vol_type,
                         {})
     default_vol_type = volume_types.get_default_volume_type()
     self.assertEqual(default_vol_type.get('name'),
                      fake_flags.def_vol_type)
    def test_default_volume_type_missing_in_db(self):
        """Test default volume type is missing in database.

        Ensures proper exception raised if default volume type
        is not in database.
        """
        default_vol_type = volume_types.get_default_volume_type()
        self.assertEqual({}, default_vol_type)
 def test_get_default_volume_type(self):
     """Ensures default volume type can be retrieved."""
     type_ref = volume_types.create(self.ctxt,
                                    conf_fixture.def_vol_type,
                                    {})
     default_vol_type = volume_types.get_default_volume_type()
     self.assertEqual(default_vol_type.get('name'),
                      conf_fixture.def_vol_type)
Exemple #7
0
    def create(self, context, size, name, description, snapshot=None,
               image_id=None, volume_type=None, metadata=None,
               availability_zone=None, source_volume=None,
               scheduler_hints=None,
               source_replica=None, consistencygroup=None,
               cgsnapshot=None, multiattach=False, source_cg=None):

        if not volume_type:
            volume_type = volume_types.get_default_volume_type()

        if self._is_lunr_volume_type(context, volume_type):
            # Lunr has size limits by volume type. Fail here instead of
            # getting an 'error' volume.
            self._validate_lunr_volume_type(volume_type, size)

            if not CONF.lunr_copy_image_enabled:
                image_id = None
            if not CONF.lunr_volume_clone_enabled:
                source_volume = None
            if snapshot:
                if self._is_lunr_volume_type(context,
                                             snapshot['volume_type_id']):
                    snapshot['volume_type_id'] = volume_type['id']
            if source_volume:
                if self._is_lunr_volume_type(context,
                                             source_volume['volume_type_id']):
                    source_volume['volume_type_id'] = volume_type['id']

        kwargs = {}
        if snapshot is not None:
            kwargs['snapshot'] = snapshot
        if image_id is not None:
            kwargs['image_id'] = image_id
        if volume_type is not None:
            kwargs['volume_type'] = volume_type
        if metadata is not None:
            kwargs['metadata'] = metadata
        if availability_zone is not None:
            kwargs['availability_zone'] = availability_zone
        if source_volume is not None:
            kwargs['source_volume'] = source_volume
        if scheduler_hints is not None:
            kwargs['scheduler_hints'] = scheduler_hints
        if multiattach is not None:
            kwargs['multiattach'] = multiattach
        if source_replica is not None:
            kwargs['source_replica'] = source_replica
        if consistencygroup is not None:
            kwargs['consistencygroup'] = consistencygroup
        if cgsnapshot is not None:
            kwargs['cgsnapshot'] = cgsnapshot
        if source_cg is not None:
            kwargs['source_cg'] = source_cg

        return super(API, self).create(context, size, name, description,
                                       **kwargs)
Exemple #8
0
    def show(self, req, id):
        """Return a single volume type item."""
        context = req.environ['cinder.context']

        # get default volume type
        if id is not None and id == 'default':
            vol_type = volume_types.get_default_volume_type()
            if not vol_type:
                msg = _("Default volume type can not be found.")
                raise exception.VolumeTypeNotFound(message=msg)
            req.cache_resource(vol_type, name='types')
        else:
            # Not found  exception will be handled at wsgi level
            vol_type = volume_types.get_volume_type(context, id)
            req.cache_resource(vol_type, name='types')

        return self._view_builder.show(req, vol_type)
Exemple #9
0
    def show(self, req, id):
        """Return a single volume type item."""
        context = req.environ['cinder.context']

        # get default volume type
        if id is not None and id == 'default':
            vol_type = volume_types.get_default_volume_type()
            if not vol_type:
                msg = _("Default volume type can not be found.")
                raise exc.HTTPNotFound(explanation=msg)
            req.cache_resource(vol_type, name='types')
        else:
            try:
                vol_type = volume_types.get_volume_type(context, id)
                req.cache_resource(vol_type, name='types')
            except exception.VolumeTypeNotFound as error:
                raise exc.HTTPNotFound(explanation=error.msg)

        return self._view_builder.show(req, vol_type)
Exemple #10
0
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata, key_manager,
                consistencygroup, cgsnapshot, group, group_snapshot, backup):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume,
                                      backup=backup)
        context.authorize(policy.CREATE_POLICY)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        backup_id = self._extract_backup(backup)
        size = self._extract_size(size, source_volume, snapshot, backup)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)
        cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot)
        group_id = self._extract_group(group)

        image_meta = self._get_image_metadata(context, image_id, size)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoids any dependency upon the encrypted volume type.
        def_vol_type = volume_types.get_default_volume_type()
        if not volume_type and not source_volume and not snapshot:
            image_volume_type = self._get_image_volume_type(context, image_id)
            volume_type = (image_volume_type
                           if image_volume_type else def_vol_type)

        availability_zones, refresh_az = self._extract_availability_zones(
            availability_zone,
            snapshot,
            source_volume,
            group,
            volume_type=volume_type)

        volume_type_id = self._get_volume_type_id(volume_type, source_volume,
                                                  snapshot)

        encryption_key_id = self._get_encryption_key_id(
            key_manager, context, volume_type_id, snapshot, source_volume,
            image_meta)

        if encryption_key_id is not None and volume_type is not None:
            extra_specs = volume_type.get('extra_specs', {})
            if extra_specs.get('multiattach', '') == '<is> True':
                msg = _('Multiattach cannot be used with encrypted volumes.')
                raise exception.InvalidVolume(reason=msg)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            if qos_specs['qos_specs']:
                specs = qos_specs['qos_specs'].get('specs', {})

            # Determine default replication status
            extra_specs = volume_types.get_volume_type_extra_specs(
                volume_type_id)
        if not specs:
            # to make sure we don't pass empty dict
            specs = None
            extra_specs = None

        if vol_utils.is_replicated_spec(extra_specs):
            replication_status = fields.ReplicationStatus.ENABLED
        else:
            replication_status = fields.ReplicationStatus.DISABLED

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
            'consistencygroup_id': consistencygroup_id,
            'cgsnapshot_id': cgsnapshot_id,
            'group_id': group_id,
            'replication_status': replication_status,
            'refresh_az': refresh_az,
            'backup_id': backup_id,
            'availability_zones': availability_zones
        }
Exemple #11
0
    def create(
        self,
        context,
        size,
        name,
        description,
        snapshot=None,
        image_id=None,
        volume_type=None,
        metadata=None,
        availability_zone=None,
        source_volume=None,
    ):

        exclusive_options = (snapshot, image_id, source_volume)
        exclusive_options_set = sum(1 for option in exclusive_options if option is not None)
        if exclusive_options_set > 1:
            msg = _("May specify only one of snapshot, imageRef " "or source volume")
            raise exception.InvalidInput(reason=msg)

        check_policy(context, "create")
        if snapshot is not None:
            if snapshot["status"] != "available":
                msg = _("status must be available")
                raise exception.InvalidSnapshot(reason=msg)
            if not size:
                size = snapshot["volume_size"]
            elif size < snapshot["volume_size"]:
                msg = _("Volume size cannot be lesser than" " the Snapshot size")
                raise exception.InvalidInput(reason=msg)
            snapshot_id = snapshot["id"]
        else:
            snapshot_id = None

        if source_volume is not None:
            if source_volume["status"] == "error":
                msg = _("Unable to clone volumes that are in an error state")
                raise exception.InvalidSourceVolume(reason=msg)
            if not size:
                size = source_volume["size"]
            else:
                if size < source_volume["size"]:
                    msg = _("Clones currently must be " ">= original volume size.")
                    raise exception.InvalidInput(reason=msg)
            source_volid = source_volume["id"]
        else:
            source_volid = 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 = _("Volume size '%s' must be an integer and greater than 0") % size
            raise exception.InvalidInput(reason=msg)

        if image_id and not (source_volume or snapshot):
            # check image existence
            image_meta = self.image_service.show(context, image_id)
            image_size_in_gb = (int(image_meta["size"]) + GB - 1) / GB
            # check image size is not larger than volume size.
            if image_size_in_gb > size:
                msg = _("Size of specified image is larger than volume size.")
                raise exception.InvalidInput(reason=msg)

        try:
            reservations = QUOTAS.reserve(context, volumes=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 volume (%(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.VolumeSizeExceedsAvailableQuota()
            elif "volumes" in overs:
                msg = _(
                    "Quota exceeded for %(s_pid)s, tried to create "
                    "volume (%(d_consumed)d volumes "
                    "already consumed)"
                )
                LOG.warn(msg % {"s_pid": context.project_id, "d_consumed": _consumed("volumes")})
                raise exception.VolumeLimitExceeded(allowed=quotas["volumes"])

        if availability_zone is None:
            availability_zone = FLAGS.storage_availability_zone

        if not volume_type and not source_volume:
            volume_type = volume_types.get_default_volume_type()

        if not volume_type and source_volume:
            volume_type_id = source_volume["volume_type_id"]
        else:
            volume_type_id = volume_type.get("id")

        self._check_metadata_properties(context, metadata)
        options = {
            "size": size,
            "user_id": context.user_id,
            "project_id": context.project_id,
            "snapshot_id": snapshot_id,
            "availability_zone": availability_zone,
            "status": "creating",
            "attach_status": "detached",
            "display_name": name,
            "display_description": description,
            "volume_type_id": volume_type_id,
            "metadata": metadata,
            "source_volid": source_volid,
        }

        try:
            volume = self.db.volume_create(context, options)
            QUOTAS.commit(context, reservations)
        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    self.db.volume_destroy(context, volume["id"])
                finally:
                    QUOTAS.rollback(context, reservations)

        request_spec = {
            "volume_properties": options,
            "volume_type": volume_type,
            "volume_id": volume["id"],
            "snapshot_id": volume["snapshot_id"],
            "image_id": image_id,
            "source_volid": volume["source_volid"],
        }

        filter_properties = {}

        self._cast_create_volume(context, request_spec, filter_properties)

        return volume
Exemple #12
0
 def test_volume_db_delete_last_type(self):
     default = volume_types.get_default_volume_type()
     self.assertRaises(exception.VolumeTypeDeletionError,
                       db.volume_type_destroy, self.ctxt,
                       default['id'])
Exemple #13
0
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata, key_manager,
                source_replica, consistencygroup, cgsnapshot):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        source_replicaid = self._extract_source_replica(source_replica)
        size = self._extract_size(size, source_volume, snapshot)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)
        cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot)

        self._check_image_metadata(context, image_id, size)

        availability_zone = self._extract_availability_zone(
            availability_zone, snapshot, source_volume)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoids any dependency upon the encrypted volume type.
        def_vol_type = volume_types.get_default_volume_type()
        if not volume_type and not source_volume and not snapshot:
            volume_type = def_vol_type

        # When creating a clone of a replica (replication test), we can't
        # use the volume type of the replica, therefore, we use the default.
        # NOTE(ronenkat): this assumes the default type is not replicated.
        if source_replicaid:
            volume_type = def_vol_type

        volume_type_id = self._get_volume_type_id(volume_type, source_volume,
                                                  snapshot)

        if image_id and volume_types.is_encrypted(context, volume_type_id):
            msg = _('Create encrypted volumes with type %(type)s '
                    'from image %(image)s is not supported.')
            msg = msg % {
                'type': volume_type_id,
                'image': image_id,
            }
            raise exception.InvalidInput(reason=msg)

        encryption_key_id = self._get_encryption_key_id(
            key_manager, context, volume_type_id, snapshot, source_volume)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            specs = qos_specs['qos_specs']
        if not specs:
            # to make sure we don't pass empty dict
            specs = None

        self._check_metadata_properties(metadata)

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'availability_zone': availability_zone,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
            'source_replicaid': source_replicaid,
            'consistencygroup_id': consistencygroup_id,
            'cgsnapshot_id': cgsnapshot_id,
        }
Exemple #14
0
    def execute(
        self,
        context,
        size,
        snapshot,
        image_id,
        source_volume,
        availability_zone,
        volume_type,
        metadata,
        key_manager,
        backup_source_volume,
        source_replica,
        consistencygroup,
    ):

        utils.check_exclusive_options(snapshot=snapshot, imageRef=image_id, source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        source_replicaid = self._extract_source_replica(source_replica)
        size = self._extract_size(size, source_volume, snapshot)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)

        self._check_image_metadata(context, image_id, size)

        availability_zone = self._extract_availability_zone(availability_zone, snapshot, source_volume)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoid any dependency upon the encrypted volume type.
        def_vol_type = volume_types.get_default_volume_type()
        if not volume_type and not source_volume and not snapshot:
            volume_type = def_vol_type

        # When creating a clone of a replica (replication test), we can't
        # use the volume type of the replica, therefore, we use the default.
        # NOTE(ronenkat): this assumes the default type is not replicated.
        if source_replicaid:
            volume_type = def_vol_type

        volume_type_id = self._get_volume_type_id(volume_type, source_volume, snapshot, backup_source_volume)

        encryption_key_id = self._get_encryption_key_id(
            key_manager, context, volume_type_id, snapshot, source_volume, backup_source_volume
        )

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            specs = qos_specs["qos_specs"]
        if not specs:
            # to make sure we don't pass empty dict
            specs = None

        self._check_metadata_properties(metadata)

        return {
            "size": size,
            "snapshot_id": snapshot_id,
            "source_volid": source_volid,
            "availability_zone": availability_zone,
            "volume_type": volume_type,
            "volume_type_id": volume_type_id,
            "encryption_key_id": encryption_key_id,
            "qos_specs": specs,
            "source_replicaid": source_replicaid,
            "consistencygroup_id": consistencygroup_id,
        }
Exemple #15
0
    def test_get_default_volume_type_under_non_default(self):
        cfg.CONF.set_default('default_volume_type', None)

        self.assertEqual({}, volume_types.get_default_volume_type())
Exemple #16
0
    def create(self, context, size, name, description, snapshot=None,
               image_id=None, volume_type=None, metadata=None,
               availability_zone=None, source_volume=None):

        exclusive_options = (snapshot, image_id, source_volume)
        exclusive_options_set = sum(1 for option in
                                    exclusive_options if option is not None)
        if exclusive_options_set > 1:
            msg = (_("May specify only one of snapshot, imageRef "
                     "or source volume"))
            raise exception.InvalidInput(reason=msg)

        check_policy(context, 'create')
        if snapshot is not None:
            if snapshot['status'] != "available":
                msg = _("status must be available")
                raise exception.InvalidSnapshot(reason=msg)
            if not size:
                size = snapshot['volume_size']
            elif size < snapshot['volume_size']:
                msg = _("Volume size cannot be lesser than"
                        " the Snapshot size")
                raise exception.InvalidInput(reason=msg)
            snapshot_id = snapshot['id']
        else:
            snapshot_id = None

        if source_volume is not None:
            if source_volume['status'] == "error":
                msg = _("Unable to clone volumes that are in an error state")
                raise exception.InvalidSourceVolume(reason=msg)
            if not size:
                size = source_volume['size']
            else:
                if size < source_volume['size']:
                    msg = _("Clones currently must be "
                            ">= original volume size.")
                    raise exception.InvalidInput(reason=msg)
            source_volid = source_volume['id']
        else:
            source_volid = 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 = (_("Volume size '%s' must be an integer and greater than 0")
                   % size)
            raise exception.InvalidInput(reason=msg)

        if (image_id and not (source_volume or snapshot)):
            # check image existence
            image_meta = self.image_service.show(context, image_id)
            image_size_in_gb = (int(image_meta['size']) + GB - 1) / GB
            #check image size is not larger than volume size.
            if image_size_in_gb > size:
                msg = _('Size of specified image is larger than volume size.')
                raise exception.InvalidInput(reason=msg)

        try:
            reservations = QUOTAS.reserve(context, volumes=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 volume (%(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.VolumeSizeExceedsAvailableQuota()
            elif 'volumes' in overs:
                msg = _("Quota exceeded for %(s_pid)s, tried to create "
                        "volume (%(d_consumed)d volumes "
                        "already consumed)")
                LOG.warn(msg % {'s_pid': context.project_id,
                                'd_consumed': _consumed('volumes')})
                raise exception.VolumeLimitExceeded(allowed=quotas['volumes'])

        if availability_zone is None:
            availability_zone = FLAGS.storage_availability_zone

        if not volume_type and not source_volume:
            volume_type = volume_types.get_default_volume_type()

        if not volume_type and source_volume:
            volume_type_id = source_volume['volume_type_id']
        else:
            volume_type_id = volume_type.get('id')

        self._check_metadata_properties(context, metadata)
        options = {'size': size,
                   'user_id': context.user_id,
                   'project_id': context.project_id,
                   'snapshot_id': snapshot_id,
                   'availability_zone': availability_zone,
                   'status': "creating",
                   'attach_status': "detached",
                   'display_name': name,
                   'display_description': description,
                   'volume_type_id': volume_type_id,
                   'metadata': metadata,
                   'source_volid': source_volid}

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

        request_spec = {'volume_properties': options,
                        'volume_type': volume_type,
                        'volume_id': volume['id'],
                        'snapshot_id': volume['snapshot_id'],
                        'image_id': image_id,
                        'source_volid': volume['source_volid']}

        filter_properties = {}

        self._cast_create_volume(context, request_spec, filter_properties)

        return volume
    def create(self, context, size, name, description, snapshot=None,
               image_id=None, volume_type=None, metadata=None,
               availability_zone=None, source_volume=None):

        if ((snapshot is not None) and (source_volume is not None)):
            msg = (_("May specify either snapshot, "
                     "or src volume but not both!"))
            raise exception.InvalidInput(reason=msg)

        check_policy(context, 'create')
        if snapshot is not None:
            if snapshot['status'] != "available":
                msg = _("status must be available")
                raise exception.InvalidSnapshot(reason=msg)
            if not size:
                size = snapshot['volume_size']

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

        if source_volume is not None:
            if source_volume['status'] == "error":
                msg = _("Unable to clone volumes that are in an error state")
                raise exception.InvalidSourceVolume(reason=msg)
            if not size:
                size = source_volume['size']
            else:
                if size < source_volume['size']:
                    msg = _("Clones currently must be "
                            ">= original volume size.")
                    raise exception.InvalidInput(reason=msg)
            source_volid = source_volume['id']
        else:
            source_volid = 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 = (_("Volume size '%s' must be an integer and greater than 0")
                   % size)
            raise exception.InvalidInput(reason=msg)

        if (image_id and not (source_volume or snapshot)):
            # check image existence
            image_meta = self.image_service.show(context, image_id)
            image_size_in_gb = (int(image_meta['size']) + GB - 1) / GB
            #check image size is not larger than volume size.
            if image_size_in_gb > size:
                msg = _('Size of specified image is larger than volume size.')
                raise exception.InvalidInput(reason=msg)

        try:
            reservations = QUOTAS.reserve(context, volumes=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 volume (%(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.VolumeSizeExceedsAvailableQuota()
            elif 'volumes' in overs:
                msg = _("Quota exceeded for %(s_pid)s, tried to create "
                        "volume (%(d_consumed)d volumes"
                        "already consumed)")
                LOG.warn(msg % {'s_pid': context.project_id,
                                'd_consumed': _consumed('volumes')})
                raise exception.VolumeLimitExceeded(allowed=quotas['volumes'])

        if availability_zone is None:
            availability_zone = FLAGS.storage_availability_zone

        if not volume_type and not source_volume:
            volume_type = volume_types.get_default_volume_type()

        if not volume_type and source_volume:
            volume_type_id = source_volume['volume_type_id']
        else:
            volume_type_id = volume_type.get('id')

        self._check_metadata_properties(context, metadata)
        options = {'size': size,
                   'user_id': context.user_id,
                   'project_id': context.project_id,
                   'snapshot_id': snapshot_id,
                   'availability_zone': availability_zone,
                   'status': "creating",
                   'attach_status': "detached",
                   'display_name': name,
                   'display_description': description,
                   'volume_type_id': volume_type_id,
                   'metadata': metadata,
                   'source_volid': source_volid}

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

        request_spec = {'volume_properties': options,
                        'volume_type': volume_type,
                        'volume_id': volume['id'],
                        'snapshot_id': volume['snapshot_id'],
                        'image_id': image_id,
                        'source_volid': volume['source_volid']}

        filter_properties = {}

        self._cast_create_volume(context, request_spec, filter_properties)

        return volume
Exemple #18
0
 def test_get_default_volume_type(self):
     """Ensures default volume type can be retrieved."""
     type_ref = volume_types.create(self.ctxt, fake_flags.def_vol_type, {})
     default_vol_type = volume_types.get_default_volume_type()
     self.assertEqual(default_vol_type.get('name'), fake_flags.def_vol_type)
Exemple #19
0
    def create(self, context, size, name, description, snapshot=None,
               image_id=None, volume_type=None, metadata=None,
               availability_zone=None, source_volume=None,
               scheduler_hints=None,
               source_replica=None, consistencygroup=None,
               cgsnapshot=None, multiattach=False, source_cg=None):

        if not volume_type:
            volume_type = volume_types.get_default_volume_type()

        if self._is_lunr_volume_type(context, volume_type):
            # Lunr has size limits by volume type. Fail here instead of
            # getting an 'error' volume.
            self._validate_lunr_volume_type(volume_type, size)

            if not CONF.lunr_copy_image_enabled:
                image_id = None
            if not CONF.lunr_volume_clone_enabled:
                source_volume = None
            if snapshot:
                if self._is_lunr_volume_type(context,
                                             snapshot['volume_type_id']):
                    snapshot['volume_type_id'] = volume_type['id']
            if source_volume:
                if self._is_lunr_volume_type(context,
                                             source_volume['volume_type_id']):
                    source_volume['volume_type_id'] = volume_type['id']
            if metadata:
                if all (k in metadata for k in ("different_node",
                                                "different_rack")):
                    msg = _("Cannot specify both different_node "
                            "and different_rack metadata keys")
                    raise exception.InvalidInput(reason=msg)

        kwargs = {}
        if snapshot is not None:
            kwargs['snapshot'] = snapshot
        if image_id is not None:
            kwargs['image_id'] = image_id
        if volume_type is not None:
            kwargs['volume_type'] = volume_type
        if metadata is not None:
            kwargs['metadata'] = metadata
        if availability_zone is not None:
            kwargs['availability_zone'] = availability_zone
        if source_volume is not None:
            kwargs['source_volume'] = source_volume
        if scheduler_hints is not None:
            kwargs['scheduler_hints'] = scheduler_hints
        if multiattach is not None:
            kwargs['multiattach'] = multiattach
        if source_replica is not None:
            kwargs['source_replica'] = source_replica
        if consistencygroup is not None:
            kwargs['consistencygroup'] = consistencygroup
        if cgsnapshot is not None:
            kwargs['cgsnapshot'] = cgsnapshot
        if source_cg is not None:
            kwargs['source_cg'] = source_cg

        return super(API, self).create(context, size, name, description,
                                       **kwargs)
Exemple #20
0
    def create(self, context, size, name, description, snapshot=None,
               image_id=None, volume_type=None, metadata=None,
               availability_zone=None, source_volume=None,
               scheduler_hints=None):

        exclusive_options = (snapshot, image_id, source_volume)
        exclusive_options_set = sum(1 for option in
                                    exclusive_options if option is not None)
        if exclusive_options_set > 1:
            msg = (_("May specify only one of snapshot, imageRef "
                     "or source volume"))
            raise exception.InvalidInput(reason=msg)

        check_policy(context, 'create')
        if snapshot is not None:
            if snapshot['status'] != "available":
                msg = _("status must be available")
                raise exception.InvalidSnapshot(reason=msg)
            if not size:
                size = snapshot['volume_size']
            elif size < snapshot['volume_size']:
                msg = _("Volume size cannot be lesser than"
                        " the Snapshot size")
                raise exception.InvalidInput(reason=msg)
            snapshot_id = snapshot['id']
        else:
            snapshot_id = None

        if source_volume is not None:
            if source_volume['status'] == "error":
                msg = _("Unable to clone volumes that are in an error state")
                raise exception.InvalidSourceVolume(reason=msg)
            if not size:
                size = source_volume['size']
            else:
                if size < source_volume['size']:
                    msg = _("Clones currently must be "
                            ">= original volume size.")
                    raise exception.InvalidInput(reason=msg)
            source_volid = source_volume['id']
        else:
            source_volid = 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 = (_("Volume size '%s' must be an integer and greater than 0")
                   % size)
            raise exception.InvalidInput(reason=msg)

        if (image_id and not (source_volume or snapshot)):
            # check image existence
            image_meta = self.image_service.show(context, image_id)
            image_size_in_gb = (int(image_meta['size']) + GB - 1) / GB
            #check image size is not larger than volume size.
            if image_size_in_gb > size:
                msg = _('Size of specified image is larger than volume size.')
                raise exception.InvalidInput(reason=msg)
            # Check image minDisk requirement is met for the particular volume
            if size < image_meta.get('min_disk', 0):
                msg = _('Image minDisk size is larger than the volume size.')
                raise exception.InvalidInput(reason=msg)

        if availability_zone is None:
            if snapshot is not None:
                availability_zone = snapshot['volume']['availability_zone']
            elif source_volume is not None:
                availability_zone = source_volume['availability_zone']
            else:
                availability_zone = CONF.storage_availability_zone
        else:
            self._check_availabilty_zone(availability_zone)

        if CONF.cloned_volume_same_az:
            if (snapshot and
                snapshot['volume']['availability_zone'] !=
                    availability_zone):
                msg = _("Volume must be in the same "
                        "availability zone as the snapshot")
                raise exception.InvalidInput(reason=msg)
            elif source_volume and \
                    source_volume['availability_zone'] != availability_zone:
                msg = _("Volume must be in the same "
                        "availability zone as the source volume")
                raise exception.InvalidInput(reason=msg)

        if not volume_type and not source_volume:
            volume_type = volume_types.get_default_volume_type()

        if not volume_type and source_volume:
            volume_type_id = source_volume['volume_type_id']
        else:
            volume_type_id = volume_type.get('id')

        try:
            reserve_opts = {'volumes': 1, 'gigabytes': size}
            QUOTAS.add_volume_type_opts(context, reserve_opts, volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        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'])

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

        self._check_metadata_properties(context, metadata)
        options = {'size': size,
                   'user_id': context.user_id,
                   'project_id': context.project_id,
                   'snapshot_id': snapshot_id,
                   'availability_zone': availability_zone,
                   'status': "creating",
                   'attach_status': "detached",
                   'display_name': name,
                   'display_description': description,
                   'volume_type_id': volume_type_id,
                   'metadata': metadata,
                   'source_volid': source_volid}

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

        request_spec = {'volume_properties': options,
                        'volume_type': volume_type,
                        'volume_id': volume['id'],
                        'snapshot_id': volume['snapshot_id'],
                        'image_id': image_id,
                        'source_volid': volume['source_volid']}

        if scheduler_hints:
            filter_properties = {'scheduler_hints': scheduler_hints}
        else:
            filter_properties = {}

        self._cast_create_volume(context, request_spec, filter_properties)

        return volume
Exemple #21
0
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata, key_manager,
                source_replica, consistencygroup, cgsnapshot, group):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        source_replicaid = self._extract_source_replica(source_replica)
        size = self._extract_size(size, source_volume, snapshot)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)
        cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot)
        group_id = self._extract_group(group)

        image_meta = self._get_image_metadata(context, image_id, size)

        availability_zone, refresh_az = self._extract_availability_zone(
            availability_zone, snapshot, source_volume, group)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoids any dependency upon the encrypted volume type.
        def_vol_type = volume_types.get_default_volume_type()
        if not volume_type and not source_volume and not snapshot:
            image_volume_type = self._get_image_volume_type(context, image_id)
            volume_type = (image_volume_type
                           if image_volume_type else def_vol_type)

        # When creating a clone of a replica (replication test), we can't
        # use the volume type of the replica, therefore, we use the default.
        # NOTE(ronenkat): this assumes the default type is not replicated.
        if source_replicaid:
            volume_type = def_vol_type

        volume_type_id = self._get_volume_type_id(volume_type, source_volume,
                                                  snapshot)

        encryption_key_id = self._get_encryption_key_id(
            key_manager, context, volume_type_id, snapshot, source_volume,
            image_meta)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            if qos_specs['qos_specs']:
                specs = qos_specs['qos_specs'].get('specs', {})

            # Determine default replication status
            extra_specs = volume_types.get_volume_type_extra_specs(
                volume_type_id)
        if not specs:
            # to make sure we don't pass empty dict
            specs = None
            extra_specs = None

        if vol_utils.is_replicated_spec(extra_specs):
            replication_status = fields.ReplicationStatus.ENABLED
        else:
            replication_status = fields.ReplicationStatus.DISABLED

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'availability_zone': availability_zone,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
            'source_replicaid': source_replicaid,
            'consistencygroup_id': consistencygroup_id,
            'cgsnapshot_id': cgsnapshot_id,
            'group_id': group_id,
            'replication_status': replication_status,
            'refresh_az': refresh_az
        }
Exemple #22
0
    def create(self, context, size, name, description, snapshot=None,
                image_id=None, volume_type=None, metadata=None,
                availability_zone=None):
        check_policy(context, 'create')
        if snapshot is not None:
            if snapshot['status'] != "available":
                msg = _("status must be available")
                raise exception.InvalidSnapshot(reason=msg)
            if not size:
                size = snapshot['volume_size']

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

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

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

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

        if image_id:
            # check image existence
            image_meta = self.image_service.show(context, image_id)
            image_size_in_gb = (int(image_meta['size']) + GB - 1) / GB
            #check image size is not larger than volume size.
            if image_size_in_gb > size:
                msg = _('Size of specified image is larger than volume size.')
                raise exception.InvalidInput(reason=msg)

        try:
            reservations = QUOTAS.reserve(context, volumes=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'])

            pid = context.project_id
            if 'gigabytes' in overs:
                consumed = _consumed('gigabytes')
                quota = quotas['gigabytes']
                LOG.warn(_("Quota exceeded for %(pid)s, tried to create "
                           "%(size)sG volume (%(consumed)dG of %(quota)dG "
                           "already consumed)") % locals())
                raise exception.VolumeSizeExceedsAvailableQuota()
            elif 'volumes' in overs:
                consumed = _consumed('volumes')
                LOG.warn(_("Quota exceeded for %(pid)s, tried to create "
                           "volume (%(consumed)d volumes already consumed)")
                           % locals())
                raise exception.VolumeLimitExceeded(allowed=quotas['volumes'])

        if availability_zone is None:
            availability_zone = FLAGS.storage_availability_zone

        if not volume_type:
            volume_type = volume_types.get_default_volume_type()

        volume_type_id = volume_type.get('id')

        options = {
            'size': size,
            'user_id': context.user_id,
            'project_id': context.project_id,
            'snapshot_id': snapshot_id,
            'availability_zone': availability_zone,
            'status': "creating",
            'attach_status': "detached",
            'display_name': name,
            'display_description': description,
            'volume_type_id': volume_type_id,
            'metadata': metadata,
            }

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

        request_spec = {
            'volume_properties': options,
            'volume_type': volume_type,
            'volume_id': volume['id'],
            'snapshot_id': volume['snapshot_id'],
            'image_id': image_id
        }

        filter_properties = {}

        self._cast_create_volume(context, request_spec, filter_properties)

        return volume
Exemple #23
0
 def test_default_volume_type_missing_in_db(self):
     """Ensures proper exception raised if default volume type
     is not in database.
     """
     default_vol_type = volume_types.get_default_volume_type()
     self.assertEqual(default_vol_type, {})
Exemple #24
0
    def create(self, req, body):
        """Creates a new volume."""

        LOG.debug('Create volume request body: %s', body)
        context = req.environ['cinder.context']

        # NOTE (pooja_jadhav) To fix bug 1774155, scheduler hints is not
        # loaded as a standard extension. If user passes
        # OS-SCH-HNT:scheduler_hints in the request body, then it will be
        # validated in the create method and this method will add
        # scheduler_hints in body['volume'].
        body = scheduler_hints.create(req, body)
        volume = body['volume']

        kwargs = {}
        self.validate_name_and_description(volume, check_length=False)

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in volume:
            volume['display_name'] = volume.pop('name')

        # NOTE(thingee): v2 API allows description instead of
        #                display_description
        if 'description' in volume:
            volume['display_description'] = volume.pop('description')

        if 'image_id' in volume:
            volume['imageRef'] = volume.pop('image_id')

        req_volume_type = volume.get('volume_type', None)
        if req_volume_type:
            # Not found exception will be handled at the wsgi level
            kwargs['volume_type'] = (objects.VolumeType.get_by_name_or_id(
                context, req_volume_type))
        else:
            kwargs['volume_type'] = (objects.VolumeType.get_by_name_or_id(
                context,
                volume_types.get_default_volume_type()['id']))

        kwargs['metadata'] = volume.get('metadata', None)

        snapshot_id = volume.get('snapshot_id')
        if snapshot_id is not None:
            # Not found exception will be handled at the wsgi level
            kwargs['snapshot'] = self.volume_api.get_snapshot(
                context, snapshot_id)
        else:
            kwargs['snapshot'] = None

        source_volid = volume.get('source_volid')
        if source_volid is not None:
            # Not found exception will be handled at the wsgi level
            kwargs['source_volume'] = \
                self.volume_api.get_volume(context,
                                           source_volid)
        else:
            kwargs['source_volume'] = None

        kwargs['group'] = None
        kwargs['consistencygroup'] = None
        consistencygroup_id = volume.get('consistencygroup_id')
        if consistencygroup_id is not None:
            # Not found exception will be handled at the wsgi level
            kwargs['group'] = self.group_api.get(context, consistencygroup_id)

        size = volume.get('size', None)
        if size is None and kwargs['snapshot'] is not None:
            size = kwargs['snapshot']['volume_size']
        elif size is None and kwargs['source_volume'] is not None:
            size = kwargs['source_volume']['size']

        LOG.info("Create volume of %s GB", size)

        image_ref = volume.get('imageRef')
        if image_ref is not None:
            image_uuid = self._image_uuid_from_ref(image_ref, context)
            kwargs['image_id'] = image_uuid

        kwargs['availability_zone'] = volume.get('availability_zone', None)
        kwargs['scheduler_hints'] = volume.get('scheduler_hints', None)
        kwargs['multiattach'] = utils.get_bool_param('multiattach', volume)

        if kwargs.get('multiattach', False):
            msg = ("The option 'multiattach' "
                   "is deprecated and will be removed in a future "
                   "release.  The default behavior going forward will "
                   "be to specify multiattach enabled volume types.")
            versionutils.report_deprecated_feature(LOG, msg)

        new_volume = self.volume_api.create(context, size,
                                            volume.get('display_name'),
                                            volume.get('display_description'),
                                            **kwargs)

        retval = self._view_builder.detail(req, new_volume)

        return retval
Exemple #25
0
    def create(self,
               context,
               size,
               name,
               description,
               snapshot=None,
               image_id=None,
               volume_type=None,
               metadata=None,
               availability_zone=None,
               source_volume=None,
               scheduler_hints=None,
               source_replica=None,
               consistencygroup=None,
               cgsnapshot=None,
               multiattach=False,
               source_cg=None):

        if not volume_type:
            volume_type = volume_types.get_default_volume_type()

        if self._is_lunr_volume_type(context, volume_type):
            # Lunr has size limits by volume type. Fail here instead of
            # getting an 'error' volume.
            self._validate_lunr_volume_type(volume_type, size)

            if not CONF.lunr_copy_image_enabled:
                image_id = None
            if not CONF.lunr_volume_clone_enabled:
                source_volume = None
            if snapshot:
                if self._is_lunr_volume_type(context,
                                             snapshot['volume_type_id']):
                    snapshot['volume_type_id'] = volume_type['id']
            if source_volume:
                # validate if source is in use
                if self._is_lunr_volume_type(context,
                                             source_volume['volume_type_id']):
                    source_volume['volume_type_id'] = volume_type['id']
            if metadata:
                if all(k in metadata
                       for k in ("different_node", "different_rack")):
                    msg = _("Cannot specify both different_node "
                            "and different_rack metadata keys")
                    raise exception.InvalidInput(reason=msg)

        kwargs = {}
        if snapshot is not None:
            kwargs['snapshot'] = snapshot
        if image_id is not None:
            kwargs['image_id'] = image_id
        if volume_type is not None:
            kwargs['volume_type'] = volume_type
        if metadata is not None:
            kwargs['metadata'] = metadata
        if availability_zone is not None:
            kwargs['availability_zone'] = availability_zone
        if source_volume is not None:
            kwargs['source_volume'] = source_volume
        if scheduler_hints is not None:
            kwargs['scheduler_hints'] = scheduler_hints
        if multiattach is not None:
            kwargs['multiattach'] = multiattach
        if source_replica is not None:
            kwargs['source_replica'] = source_replica
        if consistencygroup is not None:
            kwargs['consistencygroup'] = consistencygroup
        if cgsnapshot is not None:
            kwargs['cgsnapshot'] = cgsnapshot
        if source_cg is not None:
            kwargs['source_cg'] = source_cg

        if source_volume is not None:
            LOG.info("Finding on going operations on source volume %s." %
                     source_volume)
            siblings = self.db.snapshot_get_all_for_volume(
                context, source_volume["id"])
            in_progess_snapshots = [
                snapshot for snapshot in siblings
                if snapshot['status'] == 'creating'
            ]

            if in_progess_snapshots:
                raise SnapshotConflict(reason="Snapshot conflict",
                                       volume_id=source_volume["id"])
            self._check_clone_conflict(context, volume_id=source_volume["id"])
        return super(API, self).create(context, size, name, description,
                                       **kwargs)
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata, key_manager,
                consistencygroup, cgsnapshot, group):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        size = self._extract_size(size, source_volume, snapshot)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)
        cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot)
        group_id = self._extract_group(group)

        image_meta = self._get_image_metadata(context,
                                              image_id,
                                              size)

        availability_zone, refresh_az = self._extract_availability_zone(
            availability_zone, snapshot, source_volume, group)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoids any dependency upon the encrypted volume type.
        def_vol_type = volume_types.get_default_volume_type()
        if not volume_type and not source_volume and not snapshot:
            image_volume_type = self._get_image_volume_type(context, image_id)
            volume_type = (image_volume_type if image_volume_type else
                           def_vol_type)

        volume_type_id = self._get_volume_type_id(volume_type,
                                                  source_volume, snapshot)

        encryption_key_id = self._get_encryption_key_id(
            key_manager,
            context,
            volume_type_id,
            snapshot,
            source_volume,
            image_meta)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            if qos_specs['qos_specs']:
                specs = qos_specs['qos_specs'].get('specs', {})

            # Determine default replication status
            extra_specs = volume_types.get_volume_type_extra_specs(
                volume_type_id)
        if not specs:
            # to make sure we don't pass empty dict
            specs = None
            extra_specs = None

        if vol_utils.is_replicated_spec(extra_specs):
            replication_status = fields.ReplicationStatus.ENABLED
        else:
            replication_status = fields.ReplicationStatus.DISABLED

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'availability_zone': availability_zone,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
            'consistencygroup_id': consistencygroup_id,
            'cgsnapshot_id': cgsnapshot_id,
            'group_id': group_id,
            'replication_status': replication_status,
            'refresh_az': refresh_az
        }
Exemple #27
0
    def setUp(self):
        """Run before each test method to initialize test environment."""
        super(TestCase, self).setUp()

        # Create default notifier
        self.notifier = fake_notifier.get_fake_notifier()

        # Mock rpc get notifier with fake notifier method that joins all
        # notifications with the default notifier
        self.patch('cinder.rpc.get_notifier',
                   side_effect=self._get_joined_notifier)

        # Protect against any case where someone doesn't directly patch a retry
        # decorated call.
        self.patch('tenacity.nap.sleep')

        if self.MOCK_WORKER:
            # Mock worker creation for all tests that don't care about it
            clean_path = 'cinder.objects.cleanable.CinderCleanableObject.%s'
            for method in ('create_worker', 'set_worker', 'unset_worker'):
                self.patch(clean_path % method, return_value=None)

        if self.MOCK_TOOZ:
            self.patch('cinder.coordination.Coordinator.start')
            self.patch('cinder.coordination.Coordinator.stop')
            self.patch('cinder.coordination.Coordinator.get_lock')

        # Unit tests do not need to use lazy gettext
        i18n.enable_lazy(False)

        test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
        try:
            test_timeout = int(test_timeout)
        except ValueError:
            # If timeout value is invalid do not set a timeout.
            test_timeout = 0
        if test_timeout > 0:
            self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
        self.useFixture(fixtures.NestedTempfile())
        self.useFixture(fixtures.TempHomeDir())

        environ_enabled = (lambda var_name: strutils.bool_from_string(
            os.environ.get(var_name)))
        if environ_enabled('OS_STDOUT_CAPTURE'):
            stdout = self.useFixture(fixtures.StringStream('stdout')).stream
            self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
        if environ_enabled('OS_STDERR_CAPTURE'):
            stderr = self.useFixture(fixtures.StringStream('stderr')).stream
            self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))

        self.useFixture(log_fixture.get_logging_handle_error_fixture())
        self.useFixture(cinder_fixtures.StandardLogging())

        rpc.add_extra_exmods("cinder.tests.unit")
        self.addCleanup(rpc.clear_extra_exmods)
        self.addCleanup(rpc.cleanup)

        self.messaging_conf = messaging_conffixture.ConfFixture(CONF)
        self.messaging_conf.transport_url = 'fake:/'
        self.messaging_conf.response_timeout = 15
        self.useFixture(self.messaging_conf)

        # Load oslo_messaging_notifications config group so we can set an
        # override to prevent notifications from being ignored due to the
        # short-circuit mechanism.
        oslo_messaging.get_notification_transport(CONF)
        #  We need to use a valid driver for the notifications, so we use test.
        self.override_config('driver', ['test'],
                             group='oslo_messaging_notifications')
        rpc.init(CONF)

        # NOTE(geguileo): This is required because _determine_obj_version_cap
        # and _determine_rpc_version_cap functions in cinder.rpc.RPCAPI cache
        # versions in LAST_RPC_VERSIONS and LAST_OBJ_VERSIONS so we may have
        # weird interactions between tests if we don't clear them before each
        # test.
        rpc.LAST_OBJ_VERSIONS = {}
        rpc.LAST_RPC_VERSIONS = {}

        # Init AuthProtocol to register some base options first, such as
        # auth_url.
        auth_token.AuthProtocol('fake_app', {
            'auth_type': 'password',
            'auth_url': 'fake_url'
        })

        conf_fixture.set_defaults(CONF)
        CONF([], default_config_files=[])

        # NOTE(vish): We need a better method for creating fixtures for tests
        #             now that we have some required db setup for the system
        #             to work properly.
        self.start = timeutils.utcnow()

        CONF.set_default('connection', 'sqlite://', 'database')
        CONF.set_default('sqlite_synchronous', False, 'database')

        global _DB_CACHE
        if not _DB_CACHE:
            _DB_CACHE = Database(sqla_api,
                                 migration,
                                 sql_connection=CONF.database.connection)
        self.useFixture(_DB_CACHE)

        # NOTE(blk-u): WarningsFixture must be after the Database fixture
        # because sqlalchemy-migrate messes with the warnings filters.
        self.useFixture(cinder_fixtures.WarningsFixture())

        # NOTE(danms): Make sure to reset us back to non-remote objects
        # for each test to avoid interactions. Also, backup the object
        # registry.
        objects_base.CinderObject.indirection_api = None
        self._base_test_obj_backup = copy.copy(
            objects_base.CinderObjectRegistry._registry._obj_classes)
        self.addCleanup(self._restore_obj_registry)

        self.addCleanup(CONF.reset)
        self.addCleanup(self._common_cleanup)
        self.injected = []
        self._services = []

        fake_notifier.mock_notifier(self)

        # This will be cleaned up by the NestedTempfile fixture
        lock_path = self.useFixture(fixtures.TempDir()).path
        self.fixture = self.useFixture(config_fixture.Config(lockutils.CONF))
        self.fixture.config(lock_path=lock_path, group='oslo_concurrency')
        lockutils.set_defaults(lock_path)
        self.override_config('policy_file',
                             os.path.join(
                                 os.path.abspath(os.path.dirname(__file__)),
                                 self.POLICY_PATH),
                             group='oslo_policy')
        self.override_config('resource_query_filters_file',
                             self.RESOURCE_FILTER_PATH)
        self._disable_osprofiler()

        # NOTE(geguileo): This is required because common get_by_id method in
        # cinder.db.sqlalchemy.api caches get methods and if we use a mocked
        # get method in one test it would carry on to the next test.  So we
        # clear out the cache.
        sqla_api._GET_METHODS = {}

        self.override_config('backend_url',
                             'file://' + lock_path,
                             group='coordination')
        coordination.COORDINATOR.start()
        self.addCleanup(coordination.COORDINATOR.stop)

        if six.PY3:
            # TODO(smcginnis) Python 3 deprecates assertRaisesRegexp to
            # assertRaisesRegex, but Python 2 does not have the new name. This
            # can be removed once we stop supporting py2 or the new name is
            # added.
            self.assertRaisesRegexp = self.assertRaisesRegex

        # Ensure we have the default tpool size value and we don't carry
        # threads from other test runs.
        tpool.killall()
        tpool._nthreads = 20

        # NOTE(mikal): make sure we don't load a privsep helper accidentally
        self.useFixture(cinder_fixtures.PrivsepNoHelperFixture())

        # NOTE: This volume type is created to avoid failure at database since
        # volume_type_id is non-nullable for volumes and snapshots

        self.vt = volume_types.get_default_volume_type()
Exemple #28
0
    def test_get_default_volume_type_under_non_default(self):
        cfg.CONF.set_default('default_volume_type', None)

        self.assertEqual({}, volume_types.get_default_volume_type())
Exemple #29
0
 def test_get_default_volume_type(self):
     """Ensures default volume type can be retrieved."""
     volume_types.create(self.ctxt, conf_fixture.def_vol_type, {})
     default_vol_type = volume_types.get_default_volume_type()
     self.assertEqual(conf_fixture.def_vol_type,
                      default_vol_type.get('name'))
Exemple #30
0
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata, key_manager,
                source_replica, consistencygroup, cgsnapshot):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        source_replicaid = self._extract_source_replica(source_replica)
        size = self._extract_size(size, source_volume, snapshot)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)
        cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot)

        self._check_image_metadata(context, image_id, size)

        availability_zone = self._extract_availability_zone(availability_zone,
                                                            snapshot,
                                                            source_volume)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoids any dependency upon the encrypted volume type.
        def_vol_type = volume_types.get_default_volume_type()
        if not volume_type and not source_volume and not snapshot:
            image_volume_type = self._get_image_volume_type(context, image_id)
            volume_type = (image_volume_type if image_volume_type else
                           def_vol_type)

        # When creating a clone of a replica (replication test), we can't
        # use the volume type of the replica, therefore, we use the default.
        # NOTE(ronenkat): this assumes the default type is not replicated.
        if source_replicaid:
            volume_type = def_vol_type

        volume_type_id = self._get_volume_type_id(volume_type,
                                                  source_volume, snapshot)

        if image_id and volume_types.is_encrypted(context, volume_type_id):
            msg = _('Create encrypted volumes with type %(type)s '
                    'from image %(image)s is not supported.')
            msg = msg % {'type': volume_type_id,
                         'image': image_id, }
            raise exception.InvalidInput(reason=msg)

        encryption_key_id = self._get_encryption_key_id(key_manager,
                                                        context,
                                                        volume_type_id,
                                                        snapshot,
                                                        source_volume)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            if qos_specs['qos_specs']:
                specs = qos_specs['qos_specs'].get('specs', {})
        if not specs:
            # to make sure we don't pass empty dict
            specs = None

        utils.check_metadata_properties(metadata)

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'availability_zone': availability_zone,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
            'source_replicaid': source_replicaid,
            'consistencygroup_id': consistencygroup_id,
            'cgsnapshot_id': cgsnapshot_id,
        }
Exemple #31
0
    def create(self,
               context,
               name,
               description,
               cg_volume_types=None,
               availability_zone=None):

        check_policy(context, 'create')
        volume_type_list = None
        if cg_volume_types:
            volume_type_list = cg_volume_types.split(',')

        req_volume_types = []
        if volume_type_list:
            req_volume_types = (self.db.volume_types_get_by_name_or_id(
                context, volume_type_list))

        if not req_volume_types:
            volume_type = volume_types.get_default_volume_type()
            req_volume_types.append(volume_type)

        req_volume_type_ids = ""
        for voltype in req_volume_types:
            if voltype:
                req_volume_type_ids = (req_volume_type_ids +
                                       voltype.get('id') + ",")
        if len(req_volume_type_ids) == 0:
            req_volume_type_ids = None

        availability_zone = self._extract_availability_zone(availability_zone)

        options = {
            'user_id': context.user_id,
            'project_id': context.project_id,
            'availability_zone': availability_zone,
            'status': "creating",
            'name': name,
            'description': description,
            'volume_type_id': req_volume_type_ids
        }

        group = None
        try:
            group = self.db.consistencygroup_create(context, options)
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.error(
                    _("Error occurred when creating consistency group"
                      " %s."), name)

        request_spec_list = []
        filter_properties_list = []
        for req_volume_type in req_volume_types:
            request_spec = {
                'volume_type': req_volume_type.copy(),
                'consistencygroup_id': group['id']
            }
            filter_properties = {}
            request_spec_list.append(request_spec)
            filter_properties_list.append(filter_properties)

        # Update quota for consistencygroups
        self.update_quota(context, group['id'])

        self._cast_create_consistencygroup(context, group['id'],
                                           request_spec_list,
                                           filter_properties_list)

        return group