示例#1
0
    def test_list_volume_with_count_param_version_not_matched(self, action):
        self._create_multiple_volumes_with_different_project()

        is_detail = True if 'detail' in action else False
        req = fakes.HTTPRequest.blank("/v3/%s?with_count=True" % action)
        req.headers = mv.get_mv_header(
            mv.get_prior_version(mv.SUPPORT_COUNT_INFO))
        req.api_version_request = mv.get_api_version(
            mv.get_prior_version(mv.SUPPORT_COUNT_INFO))
        ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True)
        req.environ['cinder.context'] = ctxt
        res_dict = self.controller._get_volumes(req, is_detail=is_detail)
        self.assertNotIn('count', res_dict)
示例#2
0
    def test_list_volume_with_count_param_version_not_matched(self, action):
        self._create_multiple_volumes_with_different_project()

        is_detail = True if 'detail' in action else False
        req = fakes.HTTPRequest.blank("/v3/%s?with_count=True" % action)
        req.headers = mv.get_mv_header(
            mv.get_prior_version(mv.SUPPORT_COUNT_INFO))
        req.api_version_request = mv.get_api_version(
            mv.get_prior_version(mv.SUPPORT_COUNT_INFO))
        ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True)
        req.environ['cinder.context'] = ctxt
        res_dict = self.controller._get_volumes(req, is_detail=is_detail)
        self.assertNotIn('count', res_dict)
示例#3
0
 def test_get_manageable_snapshots_detail_previous_version(self):
     res = self._get_resp_get('fakehost',
                              True,
                              True,
                              version=mv.get_prior_version(
                                  mv.MANAGE_EXISTING_LIST))
     self.assertEqual(http_client.NOT_FOUND, res.status_int)
示例#4
0
class VolumeTransferController(volume_transfer_v2.VolumeTransferController):
    """The transfer API controller for the OpenStack API V3."""
    @wsgi.response(http_client.ACCEPTED)
    @validation.schema(volume_transfer.create, mv.BASE_VERSION,
                       mv.get_prior_version(mv.TRANSFER_WITH_SNAPSHOTS))
    @validation.schema(volume_transfer.create_v355, mv.TRANSFER_WITH_SNAPSHOTS)
    def create(self, req, body):
        """Create a new volume transfer."""
        LOG.debug('Creating new volume transfer %s', body)

        context = req.environ['cinder.context']
        transfer = body['transfer']

        volume_id = transfer['volume_id']

        name = transfer.get('name', None)
        if name is not None:
            name = name.strip()

        no_snapshots = strutils.bool_from_string(
            transfer.get('no_snapshots', False))

        LOG.info("Creating transfer of volume %s", volume_id)

        try:
            new_transfer = self.transfer_api.create(context,
                                                    volume_id,
                                                    name,
                                                    no_snapshots=no_snapshots)
        # Not found exception will be handled at the wsgi level
        except exception.Invalid as error:
            raise exc.HTTPBadRequest(explanation=error.msg)

        transfer = self._view_builder.create(req, dict(new_transfer))
        return transfer
示例#5
0
    def test_volume_types_index_with_extra_specs(self):
        req = fakes.HTTPRequest.blank(
            '/v3/%s/types?extra_specs={"key1":"value1"}' % fake.PROJECT_ID,
            use_admin_context=False)
        req.api_version_request = mv.get_api_version(mv.get_prior_version(
            mv.SUPPORT_VOLUME_TYPE_FILTER))
        res_dict = self.controller.index(req)

        # since __DEFAULT__ type always exists, total number of volume types
        # is total_types_created + 1. In this case it's 4
        self.assertEqual(4, len(res_dict['volume_types']))

        # Test filter volume type with extra specs
        req = fakes.HTTPRequest.blank(
            '/v3/%s/types?extra_specs={"key1":"value1"}' % fake.PROJECT_ID,
            use_admin_context=True)
        req.api_version_request = mv.get_api_version(
            mv.SUPPORT_VOLUME_TYPE_FILTER)
        res_dict = self.controller.index(req)
        self.assertEqual(1, len(res_dict['volume_types']))
        self.assertDictEqual({'key1': 'value1',
                              'RESKEY:availability_zones': 'az1,az2'},
                             res_dict['volume_types'][0]['extra_specs'])

        # Test filter volume type with 'availability_zones'
        req = fakes.HTTPRequest.blank(
            '/v3/%s/types?extra_specs={"RESKEY:availability_zones":"az1"}'
            % fake.PROJECT_ID, use_admin_context=True)
        req.api_version_request = mv.get_api_version(
            mv.SUPPORT_VOLUME_TYPE_FILTER)
        res_dict = self.controller.index(req)
        self.assertEqual(2, len(res_dict['volume_types']))
        self.assertEqual(
            ['volume_type1', 'volume_type2'],
            sorted([az['name'] for az in res_dict['volume_types']]))
示例#6
0
 def test_get_manageable_volumes_detail_previous_version(self):
     res = self._get_resp_get('fakehost',
                              True,
                              False,
                              version=mv.get_prior_version(
                                  mv.MANAGE_EXISTING_LIST))
     self.assertEqual(HTTPStatus.NOT_FOUND, res.status_int)
示例#7
0
    def test_snapshot_create_force_false(self, force_flag, mock_create):
        snapshot_name = 'Snapshot Test Name'
        snapshot_description = 'Snapshot Test Desc'
        snapshot = {
            "volume_id": fake.VOLUME_ID,
            "force": force_flag,
            "name": snapshot_name,
            "description": snapshot_description
        }

        body = dict(snapshot=snapshot)
        req = create_snapshot_query_with_metadata('{"key2": "val2"}',
                                                  mv.SNAPSHOT_IN_USE)
        self.assertRaises(exc.HTTPBadRequest,
                          self.controller.create,
                          req,
                          body=body)
        mock_create.assert_not_called()

        # prevent regression -- shouldn't raise for pre-mv-3.66
        req = create_snapshot_query_with_metadata(
            '{"key2": "val2"}', mv.get_prior_version(mv.SNAPSHOT_IN_USE))
        self.controller.create(req, body=body)
        # ... but also shouldn't allow an in-use snapshot
        self.assertNotIn('allow_in_use', mock_create.call_args_list[0][1])
示例#8
0
 def test_update_wrong_version(self):
     req = self._fake_update_request(
         fake.BACKUP_ID, version=mv.get_prior_version(mv.BACKUP_UPDATE))
     body = {"backup": {"name": "Updated Test Name", }}
     self.assertRaises(exception.VersionNotFoundForAPIMethod,
                       self.controller.update, req, fake.BACKUP_ID,
                       body)
    def test_update_consistencygroups_no_empty_parameters(self):
        consistencygroup = self._create_consistencygroup(
            ctxt=self.ctxt,
            status=fields.ConsistencyGroupStatus.AVAILABLE)
        req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
                                      (fake.PROJECT_ID, consistencygroup.id))
        req.environ['cinder.context'].is_admin = True

        non_supported_version = mv.get_prior_version(
            mv.CG_UPDATE_BLANK_PROPERTIES)
        req.headers = mv.get_mv_header(non_supported_version)
        req.headers['Content-Type'] = 'application/json'

        req.api_version_request = mv.get_api_version(non_supported_version)
        body = {"consistencygroup": {"name": "my_fake_cg",
                                     "description": "fake consistency group",
                                     "add_volumes": "volume-uuid-1",
                                     "remove_volumes":
                                     "volume-uuid-2, volume uuid-3", }}
        allow_empty = self.controller._check_update_parameters_v3(
            req, body['consistencygroup']['name'],
            body['consistencygroup']['description'],
            body['consistencygroup']['add_volumes'],
            body['consistencygroup']['remove_volumes'])
        self.assertEqual(False, allow_empty)
        consistencygroup.destroy()
示例#10
0
    def test_update_consistencygroup_all_empty_parameters_not_version_ok(self):
        consistencygroup = self._create_consistencygroup(
            ctxt=self.ctxt, status=fields.ConsistencyGroupStatus.AVAILABLE)
        req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
                                      (fake.PROJECT_ID, consistencygroup.id))
        req.environ['cinder.context'].is_admin = True

        non_supported_version = mv.get_prior_version(
            mv.CG_UPDATE_BLANK_PROPERTIES)
        req.headers = mv.get_mv_header(non_supported_version)
        req.api_version_request = mv.get_api_version(non_supported_version)
        req.headers['Content-Type'] = 'application/json'

        body = {
            "consistencygroup": {
                "name": None,
                "description": None,
                "add_volumes": None,
                "remove_volumes": None,
            }
        }
        self.assertRaisesRegex(
            webob.exc.HTTPBadRequest, "Name, description, "
            "add_volumes, and remove_volumes can not be "
            "all empty in the request body.", self.controller.update, req,
            consistencygroup.id, body)
        consistencygroup.destroy()
示例#11
0
    def test_volume_types_index_with_extra_specs(self):
        req = fakes.HTTPRequest.blank(
            '/v3/%s/types?extra_specs={"key1":"value1"}' % fake.PROJECT_ID,
            use_admin_context=False)
        req.api_version_request = mv.get_api_version(mv.get_prior_version(
            mv.SUPPORT_VOLUME_TYPE_FILTER))
        res_dict = self.controller.index(req)

        self.assertEqual(3, len(res_dict['volume_types']))

        # Test filter volume type with extra specs
        req = fakes.HTTPRequest.blank(
            '/v3/%s/types?extra_specs={"key1":"value1"}' % fake.PROJECT_ID,
            use_admin_context=True)
        req.api_version_request = mv.get_api_version(
            mv.SUPPORT_VOLUME_TYPE_FILTER)
        res_dict = self.controller.index(req)
        self.assertEqual(1, len(res_dict['volume_types']))
        self.assertDictEqual({'key1': 'value1',
                              'RESKEY:availability_zones': 'az1,az2'},
                             res_dict['volume_types'][0]['extra_specs'])

        # Test filter volume type with 'availability_zones'
        req = fakes.HTTPRequest.blank(
            '/v3/%s/types?extra_specs={"RESKEY:availability_zones":"az1"}'
            % fake.PROJECT_ID, use_admin_context=True)
        req.api_version_request = mv.get_api_version(
            mv.SUPPORT_VOLUME_TYPE_FILTER)
        res_dict = self.controller.index(req)
        self.assertEqual(2, len(res_dict['volume_types']))
        self.assertEqual(
            ['volume_type1', 'volume_type2'],
            sorted([az['name'] for az in res_dict['volume_types']]))
示例#12
0
    def test_update_consistencygroups_no_empty_parameters(self):
        consistencygroup = self._create_consistencygroup(
            ctxt=self.ctxt, status=fields.ConsistencyGroupStatus.AVAILABLE)
        req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
                                      (fake.PROJECT_ID, consistencygroup.id))
        req.environ['cinder.context'].is_admin = True

        non_supported_version = mv.get_prior_version(
            mv.CG_UPDATE_BLANK_PROPERTIES)
        req.headers = mv.get_mv_header(non_supported_version)
        req.headers['Content-Type'] = 'application/json'

        req.api_version_request = mv.get_api_version(non_supported_version)
        body = {
            "consistencygroup": {
                "name": "my_fake_cg",
                "description": "fake consistency group",
                "add_volumes": "volume-uuid-1",
                "remove_volumes": "volume-uuid-2, volume uuid-3",
            }
        }
        allow_empty = self.controller._check_update_parameters_v3(
            req, body['consistencygroup']['name'],
            body['consistencygroup']['description'],
            body['consistencygroup']['add_volumes'],
            body['consistencygroup']['remove_volumes'])
        self.assertEqual(False, allow_empty)
        consistencygroup.destroy()
示例#13
0
    def test_snapshot_create_allow_in_use_negative(self, mock_create):
        req = create_snapshot_query_with_metadata(
            '{"key2": "val2"}', mv.get_prior_version(mv.SNAPSHOT_IN_USE))

        body = {'snapshot': {'volume_id': fake.VOLUME_ID}}

        self.controller.create(req, body=body)
        self.assertNotIn('allow_in_use', mock_create.call_args_list[0][1])
示例#14
0
    def _expected_vol_from_controller(
            self,
            size=v2_fakes.DEFAULT_VOL_SIZE,
            availability_zone=DEFAULT_AZ,
            description=v2_fakes.DEFAULT_VOL_DESCRIPTION,
            name=v2_fakes.DEFAULT_VOL_NAME,
            consistencygroup_id=None,
            source_volid=None,
            snapshot_id=None,
            metadata=None,
            attachments=None,
            volume_type=v2_fakes.DEFAULT_VOL_TYPE,
            status=v2_fakes.DEFAULT_VOL_STATUS,
            with_migration_status=False,
            group_id=None,
            req_version=None):
        metadata = metadata or {}
        attachments = attachments or []
        volume = {'volume':
                  {'attachments': attachments,
                   'availability_zone': availability_zone,
                   'bootable': 'false',
                   'consistencygroup_id': consistencygroup_id,
                   'group_id': group_id,
                   'created_at': datetime.datetime(
                       1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC),
                   'updated_at': datetime.datetime(
                       1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC),
                   'description': description,
                   'id': v2_fakes.DEFAULT_VOL_ID,
                   'links':
                   [{'href': 'http://localhost/v3/%s/volumes/%s' % (
                             fake.PROJECT_ID, fake.VOLUME_ID),
                     'rel': 'self'},
                    {'href': 'http://localhost/%s/volumes/%s' % (
                             fake.PROJECT_ID, fake.VOLUME_ID),
                     'rel': 'bookmark'}],
                   'metadata': metadata,
                   'name': name,
                   'replication_status': 'disabled',
                   'multiattach': False,
                   'size': size,
                   'snapshot_id': snapshot_id,
                   'source_volid': source_volid,
                   'status': status,
                   'user_id': fake.USER_ID,
                   'volume_type': volume_type,
                   'encrypted': False}}

        if with_migration_status:
            volume['volume']['migration_status'] = None

        # Remove group_id if max version is less than GROUP_VOLUME.
        if req_version and req_version.matches(
                None, mv.get_prior_version(mv.GROUP_VOLUME)):
            volume['volume'].pop('group_id')

        return volume
示例#15
0
    def _expected_vol_from_controller(
            self,
            size=v2_fakes.DEFAULT_VOL_SIZE,
            availability_zone=DEFAULT_AZ,
            description=v2_fakes.DEFAULT_VOL_DESCRIPTION,
            name=v2_fakes.DEFAULT_VOL_NAME,
            consistencygroup_id=None,
            source_volid=None,
            snapshot_id=None,
            metadata=None,
            attachments=None,
            volume_type=v2_fakes.DEFAULT_VOL_TYPE,
            status=v2_fakes.DEFAULT_VOL_STATUS,
            with_migration_status=False,
            group_id=None,
            req_version=None):
        metadata = metadata or {}
        attachments = attachments or []
        volume = {'volume':
                  {'attachments': attachments,
                   'availability_zone': availability_zone,
                   'bootable': 'false',
                   'consistencygroup_id': consistencygroup_id,
                   'group_id': group_id,
                   'created_at': datetime.datetime(
                       1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC),
                   'updated_at': datetime.datetime(
                       1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC),
                   'description': description,
                   'id': v2_fakes.DEFAULT_VOL_ID,
                   'links':
                   [{'href': 'http://localhost/v3/%s/volumes/%s' % (
                             fake.PROJECT_ID, fake.VOLUME_ID),
                     'rel': 'self'},
                    {'href': 'http://localhost/%s/volumes/%s' % (
                             fake.PROJECT_ID, fake.VOLUME_ID),
                     'rel': 'bookmark'}],
                   'metadata': metadata,
                   'name': name,
                   'replication_status': 'disabled',
                   'multiattach': False,
                   'size': size,
                   'snapshot_id': snapshot_id,
                   'source_volid': source_volid,
                   'status': status,
                   'user_id': fake.USER_ID,
                   'volume_type': volume_type,
                   'encrypted': False}}

        if with_migration_status:
            volume['volume']['migration_status'] = None

        # Remove group_id if max version is less than GROUP_VOLUME.
        if req_version and req_version.matches(
                None, mv.get_prior_version(mv.GROUP_VOLUME)):
            volume['volume'].pop('group_id')

        return volume
示例#16
0
    def test_group_types_show_pre_microversion(self):
        self.mock_object(group_types, 'get_group_type',
                         return_group_types_get_group_type)

        type_id = uuid.uuid4()
        req = fakes.HTTPRequest.blank(
            '/v3/%s/group_types/%s' % (fake.PROJECT_ID, type_id),
            version=mv.get_prior_version(mv.GROUP_TYPE))

        self.assertRaises(exception.VersionNotFoundForAPIMethod,
                          self.controller.show, req, type_id)
示例#17
0
    def test_group_types_show_pre_microversion(self):
        self.mock_object(group_types, 'get_group_type',
                         return_group_types_get_group_type)

        type_id = uuid.uuid4()
        req = fakes.HTTPRequest.blank(
            '/v3/%s/group_types/%s' % (fake.PROJECT_ID, type_id),
            version=mv.get_prior_version(mv.GROUP_TYPE))

        self.assertRaises(exception.VersionNotFoundForAPIMethod,
                          self.controller.show, req, type_id)
示例#18
0
 def test_backup_show_with_metadata(self, version):
     backup = test_utils.create_backup(self.ctxt,
                                       display_name='test_backup_metadata',
                                       status=fields.BackupStatus.AVAILABLE)
     # show backup metadata
     url = '/v3/%s/backups/%s' % (fake.PROJECT_ID, backup.id)
     req = fakes.HTTPRequest.blank(url, version=version)
     backup_get = self.controller.show(req, backup.id)['backup']
     if version == mv.get_prior_version(mv.BACKUP_METADATA):
         self.assertNotIn('metadata', backup_get)
     else:
         self.assertIn('metadata', backup_get)
示例#19
0
    def test_message_list_with_general_filter(self, version, mock_update):
        url = '/v3/%s/messages' % fakes.FAKE_UUID
        req = fakes.HTTPRequest.blank(url,
                                      version=version,
                                      use_admin_context=False)
        self.controller.index(req)

        if version != mv.get_prior_version(mv.RESOURCE_FILTER):
            support_like = True if version == mv.LIKE_FILTER else False
            mock_update.assert_called_once_with(req.environ['cinder.context'],
                                                mock.ANY, 'message',
                                                support_like)
示例#20
0
    def test_attachment_list_with_general_filter(self, version, mock_update):
        url = '/v3/%s/attachments' % fake.PROJECT_ID
        req = fakes.HTTPRequest.blank(url,
                                      version=version,
                                      use_admin_context=False)
        self.controller.index(req)

        if version != mv.get_prior_version(mv.RESOURCE_FILTER):
            support_like = True if version == mv.LIKE_FILTER else False
            mock_update.assert_called_once_with(req.environ['cinder.context'],
                                                mock.ANY, 'attachment',
                                                support_like)
示例#21
0
    def _get_expected(self,
                      version=mv.get_prior_version(mv.REPLICATION_CLUSTER)):
        if (versionutils.convert_version_to_tuple(version) >=
                versionutils.convert_version_to_tuple(mv.REPLICATION_CLUSTER)):
            return EXPECTED

        expect = []
        for cluster in EXPECTED:
            cluster = cluster.copy()
            for key in ('replication_status', 'frozen', 'active_backend_id'):
                cluster.pop(key)
            expect.append(cluster)
        return expect
示例#22
0
    def _get_expected(self,
                      version=mv.get_prior_version(mv.REPLICATION_CLUSTER)):
        if (versionutils.convert_version_to_tuple(version) >=
                versionutils.convert_version_to_tuple(mv.REPLICATION_CLUSTER)):
            return EXPECTED

        expect = []
        for cluster in EXPECTED:
            cluster = cluster.copy()
            for key in ('replication_status', 'frozen', 'active_backend_id'):
                cluster.pop(key)
            expect.append(cluster)
        return expect
示例#23
0
    def test_snapshot_list_with_sort_name(self, mock_refresh):
        self._create_snapshot(name='test1')
        self._create_snapshot(name='test2')

        req = fakes.HTTPRequest.blank(
            '/v3/snapshots?sort_key=name',
            version=mv.get_prior_version(mv.SNAPSHOT_SORT))
        self.assertRaises(exception.InvalidInput, self.controller.detail, req)

        req = fakes.HTTPRequest.blank('/v3/snapshots?sort_key=name',
                                      version=mv.SNAPSHOT_SORT)
        res_dict = self.controller.detail(req)
        self.assertEqual(2, len(res_dict['snapshots']))
        self.assertEqual('test2', res_dict['snapshots'][0]['name'])
        self.assertEqual('test1', res_dict['snapshots'][1]['name'])
示例#24
0
    def test_snapshot_list_with_sort_name(self, mock_refresh):
        self._create_snapshot(name='test1')
        self._create_snapshot(name='test2')

        req = fakes.HTTPRequest.blank(
            '/v3/snapshots?sort_key=name',
            version=mv.get_prior_version(mv.SNAPSHOT_SORT))
        self.assertRaises(exception.InvalidInput, self.controller.detail, req)

        req = fakes.HTTPRequest.blank('/v3/snapshots?sort_key=name',
                                      version=mv.SNAPSHOT_SORT)
        res_dict = self.controller.detail(req)
        self.assertEqual(2, len(res_dict['snapshots']))
        self.assertEqual('test2', res_dict['snapshots'][0]['name'])
        self.assertEqual('test1', res_dict['snapshots'][1]['name'])
示例#25
0
    def test_get_all_messages_with_limit_wrong_version(self):
        self.create_message_for_tests()

        PRE_MESSAGES_PAGINATION = mv.get_prior_version(mv.MESSAGES_PAGINATION)

        url = '/v3/messages?limit=1'
        req = fakes.HTTPRequest.blank(url)
        req.method = 'GET'
        req.content_type = 'application/json'
        req.headers = mv.get_mv_header(PRE_MESSAGES_PAGINATION)
        req.api_version_request = mv.get_api_version(PRE_MESSAGES_PAGINATION)
        req.environ['cinder.context'].is_admin = True

        res = self.controller.index(req)
        self.assertEqual(4, len(res['messages']))
示例#26
0
    def _process_snapshot_filtering(self, context=None, filters=None,
                                    req_version=None):
        """Formats allowed filters"""

        # if the max version is less than SNAPSHOT_LIST_METADATA_FILTER
        # metadata based filtering is not supported
        if req_version.matches(
                None, mv.get_prior_version(mv.SNAPSHOT_LIST_METADATA_FILTER)):
            filters.pop('metadata', None)

        # Filter out invalid options
        allowed_search_options = self._get_snapshot_filter_options()

        api_utils.remove_invalid_filter_options(context, filters,
                                                allowed_search_options)
示例#27
0
    def test_get_all_messages_with_limit_wrong_version(self):
        self.create_message_for_tests()

        PRE_MESSAGES_PAGINATION = mv.get_prior_version(mv.MESSAGES_PAGINATION)

        url = '/v3/messages?limit=1'
        req = fakes.HTTPRequest.blank(url)
        req.method = 'GET'
        req.content_type = 'application/json'
        req.headers = mv.get_mv_header(PRE_MESSAGES_PAGINATION)
        req.api_version_request = mv.get_api_version(PRE_MESSAGES_PAGINATION)
        req.environ['cinder.context'].is_admin = True

        res = self.controller.index(req)
        self.assertEqual(4, len(res['messages']))
示例#28
0
    def test_snapshot_list_with_metadata_unsupported_microversion(self):
        # Create snapshot with metadata key1: value1
        metadata = {"key1": "val1"}
        self._create_snapshot(metadata=metadata)

        # Create request with metadata filter key2: value2
        req = create_snapshot_query_with_metadata(
            '{"key2":"val2"}',
            mv.get_prior_version(mv.SNAPSHOT_LIST_METADATA_FILTER))

        # query controller with above request
        res_dict = self.controller.detail(req)

        # verify some snapshot is returned
        self.assertNotEqual(0, len(res_dict['snapshots']))
示例#29
0
    def _process_snapshot_filtering(self, context=None, filters=None,
                                    req_version=None):
        """Formats allowed filters"""

        # if the max version is less than SNAPSHOT_LIST_METADATA_FILTER
        # metadata based filtering is not supported
        if req_version.matches(
                None, mv.get_prior_version(mv.SNAPSHOT_LIST_METADATA_FILTER)):
            filters.pop('metadata', None)

        # Filter out invalid options
        allowed_search_options = self._get_snapshot_filter_options()

        utils.remove_invalid_filter_options(context, filters,
                                            allowed_search_options)
示例#30
0
    def _test_list(self, get_all_mock, detailed, filters=None, expected=None,
                   version=mv.get_prior_version(mv.REPLICATION_CLUSTER)):
        filters = filters or {}
        req = FakeRequest(version=version, **filters)
        method = getattr(self.controller, 'detail' if detailed else 'index')
        clusters = method(req)

        filters = filters.copy()
        filters.setdefault('is_up', None)
        filters.setdefault('read_deleted', 'no')
        self.assertEqual(expected, clusters)
        get_all_mock.assert_called_once_with(
            req.environ['cinder.context'],
            get_services=False,
            services_summary=detailed,
            **filters)
示例#31
0
    def test_snapshot_list_with_metadata_unsupported_microversion(
            self, mock_refresh):
        # Create snapshot with metadata key1: value1
        metadata = {"key1": "val1"}
        self._create_snapshot(metadata=metadata)

        # Create request with metadata filter key2: value2
        req = create_snapshot_query_with_metadata(
            '{"key2":"val2"}',
            mv.get_prior_version(mv.SNAPSHOT_LIST_METADATA_FILTER))

        # query controller with above request
        res_dict = self.controller.detail(req)

        # verify some snapshot is returned
        self.assertNotEqual(0, len(res_dict['snapshots']))
示例#32
0
    def test_volume_types_index_with_extra_specs(self):
        def _get_volume_types(extra_specs,
                              use_admin_context=True,
                              microversion=mv.SUPPORT_VOLUME_TYPE_FILTER):
            req = fakes.HTTPRequest.blank('/v3/%s/types?extra_specs=%s' %
                                          (fake.PROJECT_ID, extra_specs),
                                          use_admin_context=use_admin_context)
            req.api_version_request = mv.get_api_version(microversion)
            res_dict = self.controller.index(req)
            return res_dict['volume_types']

        # since __DEFAULT__ type always exists, total number of volume types
        # is total_types_created + 1. In this case it's 4
        volume_types = _get_volume_types('{"key1":"value1"}',
                                         use_admin_context=False,
                                         microversion=mv.get_prior_version(
                                             mv.SUPPORT_VOLUME_TYPE_FILTER))
        self.assertEqual(4, len(volume_types))

        # Test filter volume type with extra specs
        volume_types = _get_volume_types('{"key1":"value1"}')
        self.assertEqual(1, len(volume_types))
        self.assertDictEqual(
            {
                'key1': 'value1',
                'RESKEY:availability_zones': 'az1,az2'
            }, volume_types[0]['extra_specs'])

        # Test filter volume type with 'availability_zones'
        volume_types = _get_volume_types('{"RESKEY:availability_zones":"az1"}')
        self.assertEqual(2, len(volume_types))
        self.assertEqual(['volume_type1', 'volume_type2'],
                         sorted([az['name'] for az in volume_types]))

        # Test ability for non-admin to filter with user visible extra specs
        volume_types = _get_volume_types('{"RESKEY:availability_zones":"az1"}',
                                         use_admin_context=False)
        self.assertEqual(2, len(volume_types))
        self.assertEqual(['volume_type1', 'volume_type2'],
                         sorted([az['name'] for az in volume_types]))

        # Test inability for non-admin to filter with sensitive extra specs
        volume_types = _get_volume_types('{"key1":"value1"}',
                                         use_admin_context=False)
        self.assertEqual(0, len(volume_types))
示例#33
0
 def test_backup_list_with_name(self, version):
     backup1 = test_utils.create_backup(
         self.ctxt, display_name='b_test_name',
         status=fields.BackupStatus.AVAILABLE)
     backup2 = test_utils.create_backup(
         self.ctxt, display_name='a_test_name',
         status=fields.BackupStatus.AVAILABLE)
     url = '/v3/%s/backups?sort_key=name' % fake.PROJECT_ID
     req = fakes.HTTPRequest.blank(url, version=version)
     if version == mv.get_prior_version(mv.BACKUP_SORT_NAME):
         self.assertRaises(exception.InvalidInput,
                           self.controller.index,
                           req)
     else:
         expect = backup_view.ViewBuilder().summary_list(req,
                                                         [backup1, backup2])
         result = self.controller.index(req)
         self.assertEqual(expect, result)
示例#34
0
    def test_update_consistencygroup_no_body(self):
        consistencygroup = self._create_consistencygroup(
            ctxt=self.ctxt, status=fields.ConsistencyGroupStatus.AVAILABLE)
        req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
                                      (fake.PROJECT_ID, consistencygroup.id))
        req.environ['cinder.context'].is_admin = True

        non_supported_version = mv.get_prior_version(
            mv.CG_UPDATE_BLANK_PROPERTIES)
        req.headers = mv.get_mv_header(non_supported_version)
        req.api_version_request = mv.get_api_version(non_supported_version)
        req.headers['Content-Type'] = 'application/json'

        body = None
        self.assertRaisesRegex(webob.exc.HTTPBadRequest,
                               "Missing request body", self.controller.update,
                               req, consistencygroup.id, body)
        consistencygroup.destroy()
示例#35
0
    def _test_list(self,
                   get_all_mock,
                   detailed,
                   filters=None,
                   expected=None,
                   version=mv.get_prior_version(mv.REPLICATION_CLUSTER)):
        filters = filters or {}
        req = FakeRequest(version=version, **filters)
        method = getattr(self.controller, 'detail' if detailed else 'index')
        clusters = method(req)

        filters = filters.copy()
        filters.setdefault('is_up', None)
        filters.setdefault('read_deleted', 'no')
        self.assertEqual(expected, clusters)
        get_all_mock.assert_called_once_with(req.environ['cinder.context'],
                                             get_services=False,
                                             services_summary=detailed,
                                             **filters)
示例#36
0
    def test_update_consistencygroup_no_body(self):
        consistencygroup = self._create_consistencygroup(
            ctxt=self.ctxt,
            status=fields.ConsistencyGroupStatus.AVAILABLE)
        req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
                                      (fake.PROJECT_ID, consistencygroup.id))
        req.environ['cinder.context'].is_admin = True

        non_supported_version = mv.get_prior_version(
            mv.CG_UPDATE_BLANK_PROPERTIES)
        req.headers = mv.get_mv_header(non_supported_version)
        req.api_version_request = mv.get_api_version(non_supported_version)
        req.headers['Content-Type'] = 'application/json'

        body = None
        self.assertRaisesRegexp(webob.exc.HTTPBadRequest,
                                "Missing request body",
                                self.controller.update,
                                req, consistencygroup.id, body)
        consistencygroup.destroy()
示例#37
0
    def _expected_volume_api_create_kwargs(self, snapshot=None,
                                           availability_zone=DEFAULT_AZ,
                                           source_volume=None,
                                           test_group=None,
                                           req_version=None):
        volume = {
            'metadata': None,
            'snapshot': snapshot,
            'source_volume': source_volume,
            'consistencygroup': None,
            'availability_zone': availability_zone,
            'scheduler_hints': None,
            'multiattach': False,
            'group': test_group,
        }

        # Remove group_id if max version is less than GROUP_VOLUME.
        if req_version and req_version.matches(
                None, mv.get_prior_version(mv.GROUP_VOLUME)):
            volume.pop('group')

        return volume
示例#38
0
    def index(self, req):
        """Return all global and rate limit information."""
        context = req.environ['cinder.context']
        params = req.params.copy()
        req_version = req.api_version_request

        # TODO(wangxiyuan): Support "tenant_id" here to keep the backwards
        # compatibility. Remove it once we drop all support for "tenant".
        if (req_version.matches(None,
                                mv.get_prior_version(mv.LIMITS_ADMIN_FILTER))
                or not context.is_admin):
            params.pop('project_id', None)
            params.pop('tenant_id', None)
        project_id = params.get('project_id',
                                params.get('tenant_id', context.project_id))

        quotas = QUOTAS.get_project_quotas(context, project_id, usages=False)
        abs_limits = {k: v['limit'] for k, v in quotas.items()}
        rate_limits = req.environ.get("cinder.limits", [])

        builder = self._get_view_builder(req)
        return builder.build(rate_limits, abs_limits)
示例#39
0
    def _expected_volume_api_create_kwargs(self, snapshot=None,
                                           availability_zone=DEFAULT_AZ,
                                           source_volume=None,
                                           test_group=None,
                                           req_version=None):
        volume = {
            'metadata': None,
            'snapshot': snapshot,
            'source_volume': source_volume,
            'consistencygroup': None,
            'availability_zone': availability_zone,
            'scheduler_hints': None,
            'multiattach': False,
            'group': test_group,
        }

        # Remove group_id if max version is less than GROUP_VOLUME.
        if req_version and req_version.matches(
                None, mv.get_prior_version(mv.GROUP_VOLUME)):
            volume.pop('group')

        return volume
示例#40
0
    def test_update_consistencygroup_all_empty_parameters_not_version_ok(self):
        consistencygroup = self._create_consistencygroup(
            ctxt=self.ctxt,
            status=fields.ConsistencyGroupStatus.AVAILABLE)
        req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
                                      (fake.PROJECT_ID, consistencygroup.id))
        req.environ['cinder.context'].is_admin = True

        non_supported_version = mv.get_prior_version(
            mv.CG_UPDATE_BLANK_PROPERTIES)
        req.headers = mv.get_mv_header(non_supported_version)
        req.api_version_request = mv.get_api_version(non_supported_version)
        req.headers['Content-Type'] = 'application/json'

        body = {"consistencygroup": {"name": None,
                                     "description": None,
                                     "add_volumes": None,
                                     "remove_volumes": None, }}
        self.assertRaisesRegexp(webob.exc.HTTPBadRequest, "Name, description, "
                                "add_volumes, and remove_volumes can not be "
                                "all empty in the request body.",
                                self.controller.update,
                                req, consistencygroup.id, body)
        consistencygroup.destroy()
示例#41
0
class GroupSnapshotsAPITestCase(test.TestCase):
    """Test Case for group_snapshots API."""
    def setUp(self):
        super(GroupSnapshotsAPITestCase, self).setUp()
        self.controller = v3_group_snapshots.GroupSnapshotsController()
        self.volume_api = cinder.volume.API()
        self.context = context.get_admin_context()
        self.context.project_id = fake.PROJECT_ID
        self.context.user_id = fake.USER_ID
        self.user_ctxt = context.RequestContext(fake.USER_ID,
                                                fake.PROJECT_ID,
                                                auth_token=True)
        self.group = utils.create_group(self.context,
                                        group_type_id=fake.GROUP_TYPE_ID,
                                        volume_type_ids=[fake.VOLUME_TYPE_ID])
        self.volume = utils.create_volume(self.context,
                                          group_id=self.group.id,
                                          volume_type_id=fake.VOLUME_TYPE_ID)
        self.g_snapshots_array = [
            utils.create_group_snapshot(self.context,
                                        group_id=self.group.id,
                                        group_type_id=self.group.group_type_id)
            for _ in range(3)
        ]
        self.addCleanup(self._cleanup)

    def _cleanup(self):
        for snapshot in self.g_snapshots_array:
            snapshot.destroy()
        self.volume.destroy()
        self.group.destroy()

    def test_show_group_snapshot(self):
        group_snapshot = utils.create_group_snapshot(self.context,
                                                     group_id=self.group.id)
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' %
                                      (fake.PROJECT_ID, group_snapshot.id),
                                      version=mv.GROUP_SNAPSHOTS)
        res_dict = self.controller.show(req, group_snapshot.id)

        self.assertEqual(1, len(res_dict))
        self.assertEqual('this is a test group snapshot',
                         res_dict['group_snapshot']['description'])
        self.assertEqual('test_group_snapshot',
                         res_dict['group_snapshot']['name'])
        self.assertEqual(fields.GroupSnapshotStatus.CREATING,
                         res_dict['group_snapshot']['status'])

        group_snapshot.destroy()

    @ddt.data(True, False)
    def test_list_group_snapshots_with_limit(self, is_detail):

        url = '/v3/%s/group_snapshots?limit=1' % fake.PROJECT_ID
        if is_detail:
            url = '/v3/%s/group_snapshots/detail?limit=1' % fake.PROJECT_ID
        req = fakes.HTTPRequest.blank(url,
                                      version=mv.GROUP_SNAPSHOT_PAGINATION)
        if is_detail:
            res_dict = self.controller.detail(req)
        else:
            res_dict = self.controller.index(req)

        self.assertEqual(2, len(res_dict))
        self.assertEqual(1, len(res_dict['group_snapshots']))
        self.assertEqual(self.g_snapshots_array[2].id,
                         res_dict['group_snapshots'][0]['id'])
        next_link = ('http://localhost/v3/%s/group_snapshots?limit='
                     '1&marker=%s' %
                     (fake.PROJECT_ID, res_dict['group_snapshots'][0]['id']))
        self.assertEqual(next_link,
                         res_dict['group_snapshot_links'][0]['href'])
        if is_detail:
            self.assertIn('description', res_dict['group_snapshots'][0].keys())
        else:
            self.assertNotIn('description',
                             res_dict['group_snapshots'][0].keys())

    @ddt.data(True, False)
    def test_list_group_snapshot_with_offset(self, is_detail):
        url = '/v3/%s/group_snapshots?offset=1' % fake.PROJECT_ID
        if is_detail:
            url = '/v3/%s/group_snapshots/detail?offset=1' % fake.PROJECT_ID
        req = fakes.HTTPRequest.blank(url,
                                      version=mv.GROUP_SNAPSHOT_PAGINATION)
        if is_detail:
            res_dict = self.controller.detail(req)
        else:
            res_dict = self.controller.index(req)
        self.assertEqual(1, len(res_dict))
        self.assertEqual(2, len(res_dict['group_snapshots']))
        self.assertEqual(self.g_snapshots_array[1].id,
                         res_dict['group_snapshots'][0]['id'])
        self.assertEqual(self.g_snapshots_array[0].id,
                         res_dict['group_snapshots'][1]['id'])
        if is_detail:
            self.assertIn('description', res_dict['group_snapshots'][0].keys())
        else:
            self.assertNotIn('description',
                             res_dict['group_snapshots'][0].keys())

    @ddt.data(True, False)
    def test_list_group_snapshot_with_offset_out_of_range(self, is_detail):
        url = ('/v3/%s/group_snapshots?offset=234523423455454' %
               fake.PROJECT_ID)
        if is_detail:
            url = ('/v3/%s/group_snapshots/detail?offset=234523423455454' %
                   fake.PROJECT_ID)
        req = fakes.HTTPRequest.blank(url,
                                      version=mv.GROUP_SNAPSHOT_PAGINATION)
        if is_detail:
            self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail,
                              req)
        else:
            self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index,
                              req)

    @ddt.data(False, True)
    def test_list_group_snapshot_with_limit_and_offset(self, is_detail):
        group_snapshot = utils.create_group_snapshot(
            self.context,
            group_id=self.group.id,
            group_type_id=self.group.group_type_id)
        url = '/v3/%s/group_snapshots?limit=2&offset=1' % fake.PROJECT_ID
        if is_detail:
            url = ('/v3/%s/group_snapshots/detail?limit=2&offset=1' %
                   fake.PROJECT_ID)
        req = fakes.HTTPRequest.blank(url,
                                      version=mv.GROUP_SNAPSHOT_PAGINATION)
        if is_detail:
            res_dict = self.controller.detail(req)
        else:
            res_dict = self.controller.index(req)

        self.assertEqual(2, len(res_dict))
        self.assertEqual(2, len(res_dict['group_snapshots']))
        self.assertEqual(self.g_snapshots_array[2].id,
                         res_dict['group_snapshots'][0]['id'])
        self.assertEqual(self.g_snapshots_array[1].id,
                         res_dict['group_snapshots'][1]['id'])
        self.assertIsNotNone(res_dict['group_snapshot_links'][0]['href'])
        if is_detail:
            self.assertIn('description', res_dict['group_snapshots'][0].keys())
        else:
            self.assertNotIn('description',
                             res_dict['group_snapshots'][0].keys())
        group_snapshot.destroy()

    @ddt.data(mv.get_prior_version(mv.RESOURCE_FILTER), mv.RESOURCE_FILTER,
              mv.LIKE_FILTER)
    @mock.patch('cinder.api.common.reject_invalid_filters')
    def test_group_snapshot_list_with_general_filter(self, version,
                                                     mock_update):
        url = '/v3/%s/group_snapshots' % fake.PROJECT_ID
        req = fakes.HTTPRequest.blank(url,
                                      version=version,
                                      use_admin_context=False)
        self.controller.index(req)

        if version != mv.get_prior_version(mv.RESOURCE_FILTER):
            support_like = True if version == mv.LIKE_FILTER else False
            mock_update.assert_called_once_with(req.environ['cinder.context'],
                                                mock.ANY, 'group_snapshot',
                                                support_like)

    @ddt.data(False, True)
    def test_list_group_snapshot_with_filter(self, is_detail):
        url = ('/v3/%s/group_snapshots?'
               'all_tenants=True&id=%s') % (fake.PROJECT_ID,
                                            self.g_snapshots_array[0].id)
        if is_detail:
            url = ('/v3/%s/group_snapshots/detail?'
                   'all_tenants=True&id=%s') % (fake.PROJECT_ID,
                                                self.g_snapshots_array[0].id)
        req = fakes.HTTPRequest.blank(url,
                                      version=mv.GROUP_SNAPSHOT_PAGINATION,
                                      use_admin_context=True)
        if is_detail:
            res_dict = self.controller.detail(req)
        else:
            res_dict = self.controller.index(req)

        self.assertEqual(1, len(res_dict))
        self.assertEqual(1, len(res_dict['group_snapshots']))
        self.assertEqual(self.g_snapshots_array[0].id,
                         res_dict['group_snapshots'][0]['id'])
        if is_detail:
            self.assertIn('description', res_dict['group_snapshots'][0].keys())
        else:
            self.assertNotIn('description',
                             res_dict['group_snapshots'][0].keys())

    @ddt.data(
        {
            'is_detail': True,
            'version': mv.GROUP_SNAPSHOTS
        },
        {
            'is_detail': False,
            'version': mv.GROUP_SNAPSHOTS
        },
        {
            'is_detail': True,
            'version': mv.POOL_FILTER
        },
        {
            'is_detail': False,
            'version': mv.POOL_FILTER
        },
    )
    @ddt.unpack
    def test_list_group_snapshot_with_filter_previous_version(
            self, is_detail, version):
        url = ('/v3/%s/group_snapshots?'
               'all_tenants=True&id=%s') % (fake.PROJECT_ID,
                                            self.g_snapshots_array[0].id)
        if is_detail:
            url = ('/v3/%s/group_snapshots/detail?'
                   'all_tenants=True&id=%s') % (fake.PROJECT_ID,
                                                self.g_snapshots_array[0].id)
        req = fakes.HTTPRequest.blank(url,
                                      version=version,
                                      use_admin_context=True)

        if is_detail:
            res_dict = self.controller.detail(req)
        else:
            res_dict = self.controller.index(req)

        self.assertEqual(1, len(res_dict))
        self.assertEqual(3, len(res_dict['group_snapshots']))

    @ddt.data(False, True)
    def test_list_group_snapshot_with_sort(self, is_detail):
        url = '/v3/%s/group_snapshots?sort=id:asc' % fake.PROJECT_ID
        if is_detail:
            url = ('/v3/%s/group_snapshots/detail?sort=id:asc' %
                   fake.PROJECT_ID)
        req = fakes.HTTPRequest.blank(url,
                                      version=mv.GROUP_SNAPSHOT_PAGINATION)
        expect_result = [snapshot.id for snapshot in self.g_snapshots_array]
        expect_result.sort()
        if is_detail:
            res_dict = self.controller.detail(req)
        else:
            res_dict = self.controller.index(req)
        self.assertEqual(1, len(res_dict))
        self.assertEqual(3, len(res_dict['group_snapshots']))
        self.assertEqual(expect_result[0],
                         res_dict['group_snapshots'][0]['id'])
        self.assertEqual(expect_result[1],
                         res_dict['group_snapshots'][1]['id'])
        self.assertEqual(expect_result[2],
                         res_dict['group_snapshots'][2]['id'])
        if is_detail:
            self.assertIn('description', res_dict['group_snapshots'][0].keys())
        else:
            self.assertNotIn('description',
                             res_dict['group_snapshots'][0].keys())

    def test_show_group_snapshot_with_group_snapshot_not_found(self):
        req = fakes.HTTPRequest.blank(
            '/v3/%s/group_snapshots/%s' %
            (fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID),
            version=mv.GROUP_SNAPSHOTS)
        self.assertRaises(exception.GroupSnapshotNotFound,
                          self.controller.show, req, fake.WILL_NOT_BE_FOUND_ID)

    @ddt.data(True, False)
    def test_list_group_snapshots_json(self, is_detail):
        if is_detail:
            request_url = '/v3/%s/group_snapshots/detail'
        else:
            request_url = '/v3/%s/group_snapshots'
        req = fakes.HTTPRequest.blank(request_url % fake.PROJECT_ID,
                                      version=mv.GROUP_SNAPSHOTS)
        if is_detail:
            res_dict = self.controller.detail(req)
        else:
            res_dict = self.controller.index(req)

        self.assertEqual(1, len(res_dict))
        self.assertEqual(3, len(res_dict['group_snapshots']))
        for index, snapshot in enumerate(self.g_snapshots_array):
            self.assertEqual(snapshot.id,
                             res_dict['group_snapshots'][2 - index]['id'])
            self.assertIsNotNone(res_dict['group_snapshots'][2 -
                                                             index]['name'])
            if is_detail:
                self.assertIn('description',
                              res_dict['group_snapshots'][2 - index].keys())
            else:
                self.assertNotIn('description',
                                 res_dict['group_snapshots'][2 - index].keys())

    @mock.patch('cinder.db.volume_type_get')
    @mock.patch('cinder.quota.VolumeTypeQuotaEngine.reserve')
    def test_create_group_snapshot_json(self, mock_quota, mock_vol_type):
        body = {
            "group_snapshot": {
                "name": "group_snapshot1",
                "description": "Group Snapshot 1",
                "group_id": self.group.id
            }
        }
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
                                      fake.PROJECT_ID,
                                      version=mv.GROUP_SNAPSHOTS)
        res_dict = self.controller.create(req, body=body)

        self.assertEqual(1, len(res_dict))
        self.assertIn('id', res_dict['group_snapshot'])
        group_snapshot = objects.GroupSnapshot.get_by_id(
            context.get_admin_context(), res_dict['group_snapshot']['id'])
        group_snapshot.destroy()

    @mock.patch('cinder.db.volume_type_get')
    def test_create_group_snapshot_when_volume_in_error_status(
            self, mock_vol_type):
        group = utils.create_group(
            self.context,
            group_type_id=fake.GROUP_TYPE_ID,
            volume_type_ids=[fake.VOLUME_TYPE_ID],
        )
        volume_id = utils.create_volume(
            self.context,
            status='error',
            group_id=group.id,
            volume_type_id=fake.VOLUME_TYPE_ID)['id']
        body = {
            "group_snapshot": {
                "name": "group_snapshot1",
                "description": "Group Snapshot 1",
                "group_id": group.id
            }
        }
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
                                      fake.PROJECT_ID,
                                      version=mv.GROUP_SNAPSHOTS)
        self.assertRaises(webob.exc.HTTPBadRequest,
                          self.controller.create,
                          req,
                          body=body)

        group.destroy()
        db.volume_destroy(context.get_admin_context(), volume_id)

    def test_create_group_snapshot_with_no_body(self):
        # omit body from the request
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
                                      fake.PROJECT_ID,
                                      version=mv.GROUP_SNAPSHOTS)
        self.assertRaises(exception.ValidationError,
                          self.controller.create,
                          req,
                          body=None)

    def test_create_group_snapshot_with_empty_body(self):
        # empty body in the request
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
                                      fake.PROJECT_ID,
                                      version=mv.GROUP_SNAPSHOTS)
        body = {"group_snapshot": {}}
        self.assertRaises(exception.ValidationError,
                          self.controller.create,
                          req,
                          body=body)

    @mock.patch.object(group_api.API,
                       'create_group_snapshot',
                       side_effect=exception.InvalidGroupSnapshot(
                           reason='Invalid group snapshot'))
    def test_create_with_invalid_group_snapshot(self, mock_create_group_snap):
        body = {
            "group_snapshot": {
                "name": "group_snapshot1",
                "description": "Group Snapshot 1",
                "group_id": self.group.id
            }
        }
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
                                      fake.PROJECT_ID,
                                      version=mv.GROUP_SNAPSHOTS)
        self.assertRaises(webob.exc.HTTPBadRequest,
                          self.controller.create,
                          req,
                          body=body)

    @mock.patch.object(group_api.API,
                       'create_group_snapshot',
                       side_effect=exception.GroupSnapshotNotFound(
                           group_snapshot_id='invalid_id'))
    def test_create_with_group_snapshot_not_found(self, mock_create_grp_snap):
        body = {
            "group_snapshot": {
                "name": "group_snapshot1",
                "description": "Group Snapshot 1",
                "group_id": self.group.id
            }
        }
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
                                      fake.PROJECT_ID,
                                      version=mv.GROUP_SNAPSHOTS)
        self.assertRaises(exception.GroupSnapshotNotFound,
                          self.controller.create,
                          req,
                          body=body)

    def test_create_group_snapshot_from_empty_group(self):
        empty_group = utils.create_group(self.context,
                                         group_type_id=fake.GROUP_TYPE_ID,
                                         volume_type_ids=[fake.VOLUME_TYPE_ID])
        body = {
            "group_snapshot": {
                "name": "group_snapshot1",
                "description": "Group Snapshot 1",
                "group_id": empty_group.id
            }
        }
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
                                      fake.PROJECT_ID,
                                      version=mv.GROUP_SNAPSHOTS)

        self.assertRaises(webob.exc.HTTPBadRequest,
                          self.controller.create,
                          req,
                          body=body)
        empty_group.destroy()

    def test_delete_group_snapshot_available(self):
        group_snapshot = utils.create_group_snapshot(
            self.context,
            group_id=self.group.id,
            status=fields.GroupSnapshotStatus.AVAILABLE)
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' %
                                      (fake.PROJECT_ID, group_snapshot.id),
                                      version=mv.GROUP_SNAPSHOTS)
        res_dict = self.controller.delete(req, group_snapshot.id)

        group_snapshot = objects.GroupSnapshot.get_by_id(
            self.context, group_snapshot.id)
        self.assertEqual(http_client.ACCEPTED, res_dict.status_int)
        self.assertEqual(fields.GroupSnapshotStatus.DELETING,
                         group_snapshot.status)

        group_snapshot.destroy()

    def test_delete_group_snapshot_available_used_as_source(self):
        group_snapshot = utils.create_group_snapshot(
            self.context,
            group_id=self.group.id,
            status=fields.GroupSnapshotStatus.AVAILABLE)

        group2 = utils.create_group(
            self.context,
            status='creating',
            group_snapshot_id=group_snapshot.id,
            group_type_id=fake.GROUP_TYPE_ID,
            volume_type_ids=[fake.VOLUME_TYPE_ID],
        )
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' %
                                      (fake.PROJECT_ID, group_snapshot.id),
                                      version=mv.GROUP_SNAPSHOTS)
        self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete,
                          req, group_snapshot.id)

        group_snapshot.destroy()
        group2.destroy()

    def test_delete_group_snapshot_with_group_snapshot_NotFound(self):
        req = fakes.HTTPRequest.blank(
            '/v3/%s/group_snapshots/%s' %
            (fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID),
            version=mv.GROUP_SNAPSHOTS)
        self.assertRaises(exception.GroupSnapshotNotFound,
                          self.controller.delete, req,
                          fake.WILL_NOT_BE_FOUND_ID)

    def test_delete_group_snapshot_with_invalid_group_snapshot(self):
        group_snapshot = utils.create_group_snapshot(self.context,
                                                     group_id=self.group.id,
                                                     status='invalid')
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' %
                                      (fake.PROJECT_ID, group_snapshot.id),
                                      version=mv.GROUP_SNAPSHOTS)
        self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete,
                          req, group_snapshot.id)

        group_snapshot.destroy()

    @ddt.data(
        (mv.GROUP_TYPE, 'fake_snapshot_001',
         fields.GroupSnapshotStatus.AVAILABLE,
         exception.VersionNotFoundForAPIMethod),
        (mv.get_prior_version(mv.GROUP_SNAPSHOT_RESET_STATUS),
         'fake_snapshot_001', fields.GroupSnapshotStatus.AVAILABLE,
         exception.VersionNotFoundForAPIMethod),
        (mv.GROUP_SNAPSHOT_RESET_STATUS, 'fake_snapshot_001',
         fields.GroupSnapshotStatus.AVAILABLE, exception.GroupSnapshotNotFound)
    )
    @ddt.unpack
    def test_reset_group_snapshot_status_illegal(self, version,
                                                 group_snapshot_id, status,
                                                 exceptions):
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s/action' %
                                      (fake.PROJECT_ID, group_snapshot_id),
                                      version=version)
        body = {"reset_status": {"status": status}}
        self.assertRaises(exceptions,
                          self.controller.reset_status,
                          req,
                          group_snapshot_id,
                          body=body)

    def test_reset_group_snapshot_status_invalid_status(self):
        group_snapshot = utils.create_group_snapshot(
            self.context,
            group_id=self.group.id,
            status=fields.GroupSnapshotStatus.CREATING)
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s/action' %
                                      (fake.PROJECT_ID, group_snapshot.id),
                                      version=mv.GROUP_SNAPSHOT_RESET_STATUS)
        body = {"reset_status": {"status": "invalid_test_status"}}
        self.assertRaises(exception.InvalidGroupSnapshotStatus,
                          self.controller.reset_status,
                          req,
                          group_snapshot.id,
                          body=body)
        group_snapshot.destroy()

    def test_reset_group_snapshot_status(self):
        group_snapshot = utils.create_group_snapshot(
            self.context,
            group_id=self.group.id,
            status=fields.GroupSnapshotStatus.CREATING)
        req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s/action' %
                                      (fake.PROJECT_ID, group_snapshot.id),
                                      version=mv.GROUP_SNAPSHOT_RESET_STATUS)
        body = {
            "reset_status": {
                "status": fields.GroupSnapshotStatus.AVAILABLE
            }
        }
        response = self.controller.reset_status(req,
                                                group_snapshot.id,
                                                body=body)

        g_snapshot = objects.GroupSnapshot.get_by_id(self.context,
                                                     group_snapshot.id)
        self.assertEqual(http_client.ACCEPTED, response.status_int)
        self.assertEqual(fields.GroupSnapshotStatus.AVAILABLE,
                         g_snapshot.status)
        group_snapshot.destroy()
示例#42
0
 def test_update_wrong_version(self, action):
     req = FakeRequest(version=mv.get_prior_version(mv.CLUSTER_SUPPORT))
     self.assertRaises(exception.VersionNotFoundForAPIMethod,
                       self.controller.update, req, action, {})
示例#43
0
 def test_index_wrong_version(self, detailed):
     """Verify the wrong version so that user can't list clusters."""
     self.assertRaises(exception.VersionNotFoundForAPIMethod,
                       self._test_list, detailed=detailed,
                       version=mv.get_prior_version(mv.CLUSTER_SUPPORT))
示例#44
0
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import ddt
import mock

from cinder.api import microversions as mv
from cinder.api.openstack import api_version_request as api_version
from cinder.api.v3 import limits
from cinder import test
from cinder.tests.unit.api import fakes
from cinder.tests.unit import fake_constants as fake

LIMITS_FILTER = mv.LIMITS_ADMIN_FILTER
PRE_LIMITS_FILTER = mv.get_prior_version(LIMITS_FILTER)


@ddt.ddt
class LimitsControllerTest(test.TestCase):
    def setUp(self):
        super(LimitsControllerTest, self).setUp()
        self.controller = limits.LimitsController()

    @ddt.data((PRE_LIMITS_FILTER, True), (PRE_LIMITS_FILTER, False),
              (LIMITS_FILTER, True), (LIMITS_FILTER, False))
    @mock.patch('cinder.quota.VolumeTypeQuotaEngine.get_project_quotas')
    def test_get_limit_with_project_id(self, ver_project, mock_get_quotas):
        max_ver, has_project = ver_project
        req = fakes.HTTPRequest.blank('/v3/limits', use_admin_context=True)
        if has_project:
 def test_get_backup_under_allowed_api_version(self):
     ctx = context.RequestContext(fake.USER2_ID, fake.PROJECT_ID, True)
     bak = self._send_backup_request(
         ctx, version=mv.get_prior_version(mv.BACKUP_PROJECT))
     self.assertNotIn('os-backup-project-attr:project_id', bak)
 def test_get_backup_user_id_before_microversion_v356(self):
     ctx = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True)
     bak = self._send_backup_request(
         ctx, version=mv.get_prior_version(mv.BACKUP_PROJECT_USER_ID))
     self.assertNotIn('user_id', bak)
示例#47
0
 def test_manage_snapshot_previous_version(self):
     body = {'snapshot': {'volume_id': fake.VOLUME_ID, 'ref': 'fake_ref'}}
     res = self._get_resp_post(
         body, version=mv.get_prior_version(mv.MANAGE_EXISTING_LIST))
     self.assertEqual(http_client.NOT_FOUND, res.status_int, res)
示例#48
0
 def test_get_manageable_volumes_detail_previous_version(self):
     res = self._get_resp_get(
         'fakehost', True, False,
         version=mv.get_prior_version(mv.MANAGE_EXISTING_LIST))
     self.assertEqual(http_client.NOT_FOUND, res.status_int)
示例#49
0
    def create(self, req, body):
        """Creates a new volume.

        :param req: the request
        :param body: the request body
        :returns: dict -- the new volume dictionary
        :raises HTTPNotFound, HTTPBadRequest:
        """
        self.assert_valid_body(body, 'volume')

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

        req_version = req.api_version_request
        # Remove group_id from body if max version is less than GROUP_VOLUME.
        if req_version.matches(None, mv.get_prior_version(mv.GROUP_VOLUME)):
            # NOTE(xyang): The group_id is from a group created with a
            # group_type. So with this group_id, we've got a group_type
            # for this volume. Also if group_id is passed in, that means
            # we already know which backend is hosting the group and the
            # volume will be created on the same backend as well. So it
            # won't go through the scheduler again if a group_id is
            # passed in.
            try:
                body.get('volume', {}).pop('group_id', None)
            except AttributeError:
                msg = (_("Invalid body provided for creating volume. "
                         "Request API version: %s.") % req_version)
                raise exc.HTTPBadRequest(explanation=msg)

        volume = body['volume']
        kwargs = {}
        self.validate_name_and_description(volume)

        # Check up front for legacy replication parameters to quick fail
        source_replica = volume.get('source_replica')
        if source_replica:
            msg = _("Creating a volume from a replica source was part of the "
                    "replication v1 implementation which is no longer "
                    "available.")
            raise exception.InvalidInput(reason=msg)

        # 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))

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

        snapshot_id = volume.get('snapshot_id')
        if snapshot_id is not None:
            if not uuidutils.is_uuid_like(snapshot_id):
                msg = _("Snapshot ID must be in UUID form.")
                raise exc.HTTPBadRequest(explanation=msg)
            # 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:
            if not uuidutils.is_uuid_like(source_volid):
                msg = _("Source volume ID '%s' must be a "
                        "valid UUID.") % source_volid
                raise exc.HTTPBadRequest(explanation=msg)
            # 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:
            if not uuidutils.is_uuid_like(consistencygroup_id):
                msg = _("Consistency group ID '%s' must be a "
                        "valid UUID.") % consistencygroup_id
                raise exc.HTTPBadRequest(explanation=msg)
            # Not found exception will be handled at the wsgi level
            kwargs['group'] = self.group_api.get(context, consistencygroup_id)

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

        if self.ext_mgr.is_loaded('os-image-create'):
            image_ref = volume.get('imageRef')
            if image_ref is not None:
                image_uuid = self._image_uuid_from_ref(image_ref, context)
                image_snapshot = self._get_image_snapshot(context, image_uuid)
                if (req_version.matches(mv.get_api_version(
                        mv.SUPPORT_NOVA_IMAGE)) and image_snapshot):
                    kwargs['snapshot'] = image_snapshot
                else:
                    kwargs['image_id'] = image_uuid

        # Add backup if min version is greater than or equal
        # to VOLUME_CREATE_FROM_BACKUP.
        if req_version.matches(mv.VOLUME_CREATE_FROM_BACKUP, None):
            backup_id = volume.get('backup_id')
            if backup_id:
                if not uuidutils.is_uuid_like(backup_id):
                    msg = _("Backup ID must be in UUID form.")
                    raise exc.HTTPBadRequest(explanation=msg)
                kwargs['backup'] = self.backup_api.get(context,
                                                       backup_id=backup_id)
            else:
                kwargs['backup'] = None

        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']
        elif size is None and kwargs.get('backup') is not None:
            size = kwargs['backup']['size']

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

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

        if multiattach:
            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
示例#50
0
 def test_cleanup_old_api_version(self, rpc_mock):
     res = self._get_resp_post({}, mv.get_prior_version(mv.WORKERS_CLEANUP))
     self.assertEqual(http_client.NOT_FOUND, res.status_code)
     rpc_mock.assert_not_called()
示例#51
0
 def test_update_wrong_version(self, action):
     req = FakeRequest(version=mv.get_prior_version(mv.CLUSTER_SUPPORT))
     self.assertRaises(exception.VersionNotFoundForAPIMethod,
                       self.controller.update, req, action, {})
示例#52
0
 def test_manage_snapshot_previous_version(self):
     body = {'snapshot': {'volume_id': fake.VOLUME_ID, 'ref': 'fake_ref'}}
     res = self._get_resp_post(body,
                               version=mv.get_prior_version(
                                   mv.MANAGE_EXISTING_LIST))
     self.assertEqual(http_client.NOT_FOUND, res.status_int, res)
 def test_get_backup_under_allowed_api_version(self):
     ctx = context.RequestContext(fake.USER2_ID, fake.PROJECT_ID, True)
     bak = self._send_backup_request(ctx,
                                     version=mv.get_prior_version(
                                         mv.BACKUP_PROJECT))
     self.assertNotIn('os-backup-project-attr:project_id', bak)
示例#54
0
 def test_volumes_summary_in_unsupport_version(self):
     """Function call to test summary volumes API in unsupported version"""
     req = self._fake_volumes_summary_request(
         version=mv.get_prior_version(mv.VOLUME_SUMMARY))
     self.assertRaises(exception.VersionNotFoundForAPIMethod,
                       self.controller.summary, req)
示例#55
0
class VolumeController(volumes_v2.VolumeController):
    """The Volumes API controller for the OpenStack API V3."""

    _view_builder_class = volume_views_v3.ViewBuilder

    def __init__(self, ext_mgr):
        self.group_api = group_api.API()
        self.backup_api = backup_api.API()
        super(VolumeController, self).__init__(ext_mgr)

    def delete(self, req, id):
        """Delete a volume."""
        context = req.environ['cinder.context']
        req_version = req.api_version_request

        cascade = utils.get_bool_param('cascade', req.params)
        force = False

        params = ""
        if req_version.matches(mv.VOLUME_DELETE_FORCE):
            force = utils.get_bool_param('force', req.params)
            if cascade or force:
                params = "(cascade: %(c)s, force: %(f)s)" % {
                    'c': cascade,
                    'f': force
                }

        LOG.info("Delete volume with id: %(id)s %(params)s", {
            'id': id,
            'params': params
        },
                 context=context)

        volume = self.volume_api.get(context, id)

        if force:
            context.authorize(policy.FORCE_DELETE_POLICY, target_obj=volume)

        self.volume_api.delete(context, volume, cascade=cascade, force=force)

        return webob.Response(status_int=http_client.ACCEPTED)

    @common.process_general_filtering('volume')
    def _process_volume_filtering(self,
                                  context=None,
                                  filters=None,
                                  req_version=None):
        if req_version.matches(None, mv.MESSAGES):
            filters.pop('glance_metadata', None)

        if req_version.matches(None, mv.BACKUP_UPDATE):
            filters.pop('group_id', None)

        utils.remove_invalid_filter_options(context, filters,
                                            self._get_volume_filter_options())

    def _get_volumes(self, req, is_detail):
        """Returns a list of volumes, transformed through view builder."""

        context = req.environ['cinder.context']
        req_version = req.api_version_request

        params = req.params.copy()
        marker, limit, offset = common.get_pagination_params(params)
        sort_keys, sort_dirs = common.get_sort_params(params)
        filters = params

        show_count = False
        if req_version.matches(
                mv.SUPPORT_COUNT_INFO) and 'with_count' in filters:
            show_count = utils.get_bool_param('with_count', filters)
            filters.pop('with_count')

        self._process_volume_filtering(context=context,
                                       filters=filters,
                                       req_version=req_version)

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

        if 'name' in filters:
            filters['display_name'] = filters.pop('name')

        strict = req.api_version_request.matches(mv.VOLUME_LIST_BOOTABLE, None)
        self.volume_api.check_volume_filters(filters, strict)

        volumes = self.volume_api.get_all(context,
                                          marker,
                                          limit,
                                          sort_keys=sort_keys,
                                          sort_dirs=sort_dirs,
                                          filters=filters.copy(),
                                          viewable_admin_meta=True,
                                          offset=offset)
        total_count = None
        if show_count:
            total_count = self.volume_api.calculate_resource_count(
                context, 'volume', filters)

        for volume in volumes:
            utils.add_visible_admin_metadata(volume)

        req.cache_db_volumes(volumes.objects)

        if is_detail:
            volumes = self._view_builder.detail_list(req, volumes, total_count)
        else:
            volumes = self._view_builder.summary_list(req, volumes,
                                                      total_count)
        return volumes

    @wsgi.Controller.api_version(mv.VOLUME_SUMMARY)
    def summary(self, req):
        """Return summary of volumes."""
        view_builder_v3 = volume_views_v3.ViewBuilder()
        context = req.environ['cinder.context']
        filters = req.params.copy()

        utils.remove_invalid_filter_options(context, filters,
                                            self._get_volume_filter_options())

        num_vols, sum_size, metadata = self.volume_api.get_volume_summary(
            context, filters=filters)

        req_version = req.api_version_request
        if req_version.matches(mv.VOLUME_SUMMARY_METADATA):
            all_distinct_metadata = metadata
        else:
            all_distinct_metadata = None

        return view_builder_v3.quick_summary(num_vols, int(sum_size),
                                             all_distinct_metadata)

    @wsgi.response(http_client.ACCEPTED)
    @wsgi.Controller.api_version(mv.VOLUME_REVERT)
    @wsgi.action('revert')
    def revert(self, req, id, body):
        """revert a volume to a snapshot"""

        context = req.environ['cinder.context']
        self.assert_valid_body(body, 'revert')
        snapshot_id = body['revert'].get('snapshot_id')
        volume = self.volume_api.get_volume(context, id)
        try:
            l_snap = volume.get_latest_snapshot()
        except exception.VolumeSnapshotNotFound:
            msg = _("Volume %s doesn't have any snapshots.")
            raise exc.HTTPBadRequest(explanation=msg % volume.id)
        # Ensure volume and snapshot match.
        if snapshot_id is None or snapshot_id != l_snap.id:
            msg = _("Specified snapshot %(s_id)s is None or not "
                    "the latest one of volume %(v_id)s.")
            raise exc.HTTPBadRequest(explanation=msg % {
                's_id': snapshot_id,
                'v_id': volume.id
            })
        if volume.size != l_snap.volume_size:
            msg = _("Can't revert volume %(v_id)s to its latest snapshot "
                    "%(s_id)s. The volume size must be equal to the snapshot "
                    "size.")
            raise exc.HTTPBadRequest(explanation=msg % {
                's_id': snapshot_id,
                'v_id': volume.id
            })
        try:
            msg = 'Reverting volume %(v_id)s to snapshot %(s_id)s.'
            LOG.info(msg, {'v_id': volume.id, 's_id': l_snap.id})
            self.volume_api.revert_to_snapshot(context, volume, l_snap)
        except (exception.InvalidVolume, exception.InvalidSnapshot) as e:
            raise exc.HTTPConflict(explanation=six.text_type(e))
        except exception.VolumeSizeExceedsAvailableQuota as e:
            raise exc.HTTPForbidden(explanation=six.text_type(e))

    def _get_image_snapshot(self, context, image_uuid):
        image_snapshot = None
        if image_uuid:
            image_service = glance.get_default_image_service()
            image_meta = image_service.show(context, image_uuid)
            if image_meta is not None:
                bdms = image_meta.get('properties',
                                      {}).get('block_device_mapping', [])
                if bdms:
                    boot_bdm = [
                        bdm for bdm in bdms
                        if (bdm.get('source_type') == 'snapshot'
                            and bdm.get('boot_index') == 0)
                    ]
                    if boot_bdm:
                        try:
                            image_snapshot = self.volume_api.get_snapshot(
                                context, boot_bdm[0].get('snapshot_id'))
                            return image_snapshot
                        except exception.NotFound:
                            explanation = _(
                                'Nova specific image is found, but boot '
                                'volume snapshot id:%s not found.'
                            ) % boot_bdm[0].get('snapshot_id')
                            raise exc.HTTPNotFound(explanation=explanation)
            return image_snapshot

    @wsgi.response(http_client.ACCEPTED)
    @validation.schema(volumes.create, mv.BASE_VERSION,
                       mv.get_prior_version(mv.GROUP_VOLUME))
    @validation.schema(volumes.create_volume_v313, mv.GROUP_VOLUME,
                       mv.get_prior_version(mv.VOLUME_CREATE_FROM_BACKUP))
    @validation.schema(volumes.create_volume_v347,
                       mv.VOLUME_CREATE_FROM_BACKUP,
                       mv.get_prior_version(mv.SUPPORT_VOLUME_SCHEMA_CHANGES))
    @validation.schema(volumes.create_volume_v353,
                       mv.SUPPORT_VOLUME_SCHEMA_CHANGES)
    def create(self, req, body):
        """Creates a new volume.

        :param req: the request
        :param body: the request body
        :returns: dict -- the new volume dictionary
        :raises HTTPNotFound, HTTPBadRequest:
        """
        LOG.debug('Create volume request body: %s', body)
        context = req.environ['cinder.context']

        req_version = req.api_version_request

        # 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))

        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)

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

        image_ref = volume.get('imageRef')
        if image_ref is not None:
            image_uuid = self._image_uuid_from_ref(image_ref, context)
            image_snapshot = self._get_image_snapshot(context, image_uuid)
            if (req_version.matches(mv.get_api_version(mv.SUPPORT_NOVA_IMAGE))
                    and image_snapshot):
                kwargs['snapshot'] = image_snapshot
            else:
                kwargs['image_id'] = image_uuid

        backup_id = volume.get('backup_id')
        if backup_id:
            kwargs['backup'] = self.backup_api.get(context,
                                                   backup_id=backup_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']
        elif size is None and kwargs.get('backup') is not None:
            size = kwargs['backup']['size']

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

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

        if multiattach:
            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
示例#56
0
 def test_show_wrong_version(self):
     req = FakeRequest(version=mv.get_prior_version(mv.CLUSTER_SUPPORT))
     self.assertRaises(exception.VersionNotFoundForAPIMethod,
                       self.controller.show, req, 'name')