Merge "Switch request_spec in create volume calls to ovo"
This commit is contained in:
commit
16f6867ea0
@ -29,6 +29,7 @@ def register_all():
|
||||
__import__('cinder.objects.cluster')
|
||||
__import__('cinder.objects.consistencygroup')
|
||||
__import__('cinder.objects.qos_specs')
|
||||
__import__('cinder.objects.request_spec')
|
||||
__import__('cinder.objects.service')
|
||||
__import__('cinder.objects.snapshot')
|
||||
__import__('cinder.objects.volume')
|
||||
|
@ -106,6 +106,7 @@ OBJ_VERSIONS.add('1.6', {'QualityOfServiceSpecs': '1.0',
|
||||
OBJ_VERSIONS.add('1.7', {'Cluster': '1.0', 'ClusterList': '1.0',
|
||||
'Service': '1.4', 'Volume': '1.4',
|
||||
'ConsistencyGroup': '1.3'})
|
||||
OBJ_VERSIONS.add('1.8', {'RequestSpec': '1.0', 'VolumeProperties': '1.0'})
|
||||
|
||||
|
||||
class CinderObjectRegistry(base.VersionedObjectRegistry):
|
||||
|
126
cinder/objects/request_spec.py
Normal file
126
cinder/objects/request_spec.py
Normal file
@ -0,0 +1,126 @@
|
||||
# Copyright 2016 Intel Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_versionedobjects import fields
|
||||
|
||||
from cinder import objects
|
||||
from cinder.objects import base
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@base.CinderObjectRegistry.register
|
||||
class RequestSpec(base.CinderObject, base.CinderObjectDictCompat,
|
||||
base.CinderComparableObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'consistencygroup_id': fields.UUIDField(nullable=True),
|
||||
'cgsnapshot_id': fields.UUIDField(nullable=True),
|
||||
'image_id': fields.UUIDField(nullable=True),
|
||||
'snapshot_id': fields.UUIDField(nullable=True),
|
||||
'source_replicaid': fields.UUIDField(nullable=True),
|
||||
'source_volid': fields.UUIDField(nullable=True),
|
||||
'volume_id': fields.UUIDField(nullable=True),
|
||||
'volume': fields.ObjectField('Volume', nullable=True),
|
||||
'volume_type': fields.ObjectField('VolumeType', nullable=True),
|
||||
'volume_properties': fields.ObjectField('VolumeProperties',
|
||||
nullable=True),
|
||||
'CG_backend': fields.StringField(nullable=True),
|
||||
}
|
||||
|
||||
obj_extra_fields = ['resource_properties']
|
||||
|
||||
@property
|
||||
def resource_properties(self):
|
||||
# TODO(dulek): This is to maintain compatibility with filters from
|
||||
# oslo-incubator. As we've moved them into our codebase we should adapt
|
||||
# them to use volume_properties and remove this shim.
|
||||
return self.volume_properties
|
||||
|
||||
@classmethod
|
||||
def from_primitives(cls, spec):
|
||||
"""Returns RequestSpec object creating it from legacy dictionary.
|
||||
|
||||
FIXME(dulek): This should go away in early O as we stop supporting
|
||||
backward compatibility with M.
|
||||
"""
|
||||
spec = spec.copy()
|
||||
spec_obj = cls()
|
||||
|
||||
vol_props = spec.pop('volume_properties', {})
|
||||
if vol_props is not None:
|
||||
vol_props = VolumeProperties(**vol_props)
|
||||
spec_obj.volume_properties = vol_props
|
||||
|
||||
if 'volume' in spec:
|
||||
vol = spec.pop('volume', {})
|
||||
vol.pop('name', None)
|
||||
if vol is not None:
|
||||
vol = objects.Volume(**vol)
|
||||
spec_obj.volume = vol
|
||||
|
||||
if 'volume_type' in spec:
|
||||
vol_type = spec.pop('volume_type', {})
|
||||
if vol_type is not None:
|
||||
vol_type = objects.VolumeType(**vol_type)
|
||||
spec_obj.volume_type = vol_type
|
||||
|
||||
spec.pop('resource_properties', None)
|
||||
|
||||
for k, v in spec.items():
|
||||
setattr(spec_obj, k, v)
|
||||
|
||||
return spec_obj
|
||||
|
||||
|
||||
@base.CinderObjectRegistry.register
|
||||
class VolumeProperties(base.CinderObject, base.CinderObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
# TODO(dulek): We should add this to initially move volume_properites to
|
||||
# ovo, but this should be removed as soon as possible. Most of the data
|
||||
# here is already in request_spec and volume there. Outstanding ones would
|
||||
# be reservation, and qos_specs. First one may be moved to request_spec and
|
||||
# second added as relationship in volume_type field and whole
|
||||
# volume_properties (and resource_properties) in request_spec won't be
|
||||
# needed.
|
||||
|
||||
fields = {
|
||||
'attach_status': fields.StringField(nullable=True),
|
||||
'availability_zone': fields.StringField(nullable=True),
|
||||
'cgsnapshot_id': fields.UUIDField(nullable=True),
|
||||
'consistencygroup_id': fields.UUIDField(nullable=True),
|
||||
'display_description': fields.StringField(nullable=True),
|
||||
'display_name': fields.StringField(nullable=True),
|
||||
'encryption_key_id': fields.UUIDField(nullable=True),
|
||||
'metadata': fields.DictOfStringsField(nullable=True),
|
||||
'multiattach': fields.BooleanField(nullable=True),
|
||||
'project_id': fields.StringField(nullable=True),
|
||||
'qos_specs': fields.DictOfStringsField(nullable=True),
|
||||
'replication_status': fields.StringField(nullable=True),
|
||||
'reservations': fields.ListOfStringsField(nullable=True),
|
||||
'size': fields.IntegerField(nullable=True),
|
||||
'snapshot_id': fields.UUIDField(nullable=True),
|
||||
'source_replicaid': fields.UUIDField(nullable=True),
|
||||
'source_volid': fields.UUIDField(nullable=True),
|
||||
'status': fields.StringField(nullable=True),
|
||||
'user_id': fields.StringField(nullable=True),
|
||||
'volume_type_id': fields.UUIDField(nullable=True),
|
||||
}
|
@ -65,7 +65,7 @@ class VolumeType(base.CinderPersistentObject, base.CinderObject,
|
||||
@staticmethod
|
||||
def _from_db_object(context, type, db_type, expected_attrs=None):
|
||||
if expected_attrs is None:
|
||||
expected_attrs = []
|
||||
expected_attrs = ['extra_specs', 'projects']
|
||||
for name, field in type.fields.items():
|
||||
if name in OPTIONAL_FIELDS:
|
||||
continue
|
||||
|
@ -22,6 +22,7 @@ Weighing Functions.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LE, _LW
|
||||
@ -257,27 +258,26 @@ class FilterScheduler(driver.Scheduler):
|
||||
"""
|
||||
elevated = context.elevated()
|
||||
|
||||
volume_properties = request_spec['volume_properties']
|
||||
# Since Cinder is using mixed filters from Oslo and it's own, which
|
||||
# takes 'resource_XX' and 'volume_XX' as input respectively, copying
|
||||
# 'volume_XX' to 'resource_XX' will make both filters happy.
|
||||
resource_properties = volume_properties.copy()
|
||||
volume_type = request_spec.get("volume_type", None)
|
||||
resource_type = request_spec.get("volume_type", None)
|
||||
request_spec.update({'resource_properties': resource_properties})
|
||||
volume_type = resource_type = request_spec.get("volume_type")
|
||||
|
||||
config_options = self._get_configuration_options()
|
||||
|
||||
if filter_properties is None:
|
||||
filter_properties = {}
|
||||
self._populate_retry(filter_properties, resource_properties)
|
||||
self._populate_retry(filter_properties,
|
||||
request_spec['volume_properties'])
|
||||
|
||||
if resource_type is None:
|
||||
msg = _("volume_type cannot be None")
|
||||
raise exception.InvalidVolumeType(reason=msg)
|
||||
|
||||
request_spec_dict = jsonutils.to_primitive(request_spec)
|
||||
|
||||
filter_properties.update({'context': context,
|
||||
'request_spec': request_spec,
|
||||
'request_spec': request_spec_dict,
|
||||
'config_options': config_options,
|
||||
'volume_type': volume_type,
|
||||
'resource_type': resource_type})
|
||||
@ -288,7 +288,8 @@ class FilterScheduler(driver.Scheduler):
|
||||
# If multiattach is enabled on a volume, we need to add
|
||||
# multiattach to extra specs, so that the capability
|
||||
# filtering is enabled.
|
||||
multiattach = volume_properties.get('multiattach', False)
|
||||
multiattach = request_spec['volume_properties'].get('multiattach',
|
||||
False)
|
||||
if multiattach and 'multiattach' not in resource_type.get(
|
||||
'extra_specs', {}):
|
||||
if 'extra_specs' not in resource_type:
|
||||
|
@ -136,6 +136,11 @@ class SchedulerManager(manager.Manager):
|
||||
# volume by its volume_id.
|
||||
volume = objects.Volume.get_by_id(context, volume_id)
|
||||
|
||||
# FIXME(dulek): Remove this in v3.0 of RPC API.
|
||||
if isinstance(request_spec, dict):
|
||||
# We may receive request_spec as dict from older clients.
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
|
||||
try:
|
||||
flow_engine = create_volume.get_flow(context,
|
||||
db, self.driver,
|
||||
|
@ -53,9 +53,10 @@ class SchedulerAPI(rpc.RPCAPI):
|
||||
|
||||
2.0 - Remove 1.x compatibility
|
||||
2.1 - Adds support for sending objects over RPC in manage_existing()
|
||||
2.2 - Sends request_spec as object in create_volume()
|
||||
"""
|
||||
|
||||
RPC_API_VERSION = '2.1'
|
||||
RPC_API_VERSION = '2.2'
|
||||
TOPIC = CONF.scheduler_topic
|
||||
BINARY = 'cinder-scheduler'
|
||||
|
||||
@ -87,7 +88,11 @@ class SchedulerAPI(rpc.RPCAPI):
|
||||
'snapshot_id': snapshot_id, 'image_id': image_id,
|
||||
'request_spec': request_spec_p,
|
||||
'filter_properties': filter_properties, 'volume': volume}
|
||||
version = '2.0'
|
||||
version = '2.2'
|
||||
if not self.client.can_send_version('2.2'):
|
||||
# Send request_spec as dict
|
||||
version = '2.0'
|
||||
msg_args['request_spec'] = jsonutils.to_primitive(request_spec)
|
||||
|
||||
cctxt = self.client.prepare(version=version)
|
||||
return cctxt.cast(ctxt, 'create_volume', **msg_args)
|
||||
|
@ -34,6 +34,7 @@ object_data = {
|
||||
'ConsistencyGroupList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
|
||||
'QualityOfServiceSpecs': '1.0-0b212e0a86ee99092229874e03207fe8',
|
||||
'QualityOfServiceSpecsList': '1.0-1b54e51ad0fc1f3a8878f5010e7e16dc',
|
||||
'RequestSpec': '1.0-42685a616bd27c2a4d75cba93a81ed8c',
|
||||
'Service': '1.4-c7d011989d1718ca0496ccf640b42712',
|
||||
'ServiceList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
|
||||
'Snapshot': '1.1-37966f7141646eb29e9ad5298ff2ca8a',
|
||||
@ -42,6 +43,7 @@ object_data = {
|
||||
'VolumeList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
|
||||
'VolumeAttachment': '1.0-b30dacf62b2030dd83d8a1603f1064ff',
|
||||
'VolumeAttachmentList': '1.0-15ecf022a68ddbb8c2a6739cfc9f8f5e',
|
||||
'VolumeProperties': '1.0-42f00cf1f6c657377a3e2a7efbed0bca',
|
||||
'VolumeType': '1.2-02ecb0baac87528d041f4ddd95b95579',
|
||||
'VolumeTypeList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
|
||||
}
|
||||
@ -90,7 +92,7 @@ class TestObjectVersions(test.TestCase):
|
||||
|
||||
classes = base.CinderObjectRegistry.obj_classes()
|
||||
for name, cls in classes.items():
|
||||
if not issubclass(cls[0], base.ObjectListBase):
|
||||
if issubclass(cls[0], base.CinderPersistentObject):
|
||||
db_model = db.get_model_for_versioned_object(cls[0])
|
||||
_check_table_matched(db_model, cls[0])
|
||||
|
||||
|
@ -20,8 +20,10 @@ import mock
|
||||
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
from cinder import objects
|
||||
from cinder.scheduler import filter_scheduler
|
||||
from cinder.scheduler import host_manager
|
||||
from cinder.tests.unit import fake_constants as fake
|
||||
from cinder.tests.unit.scheduler import fakes
|
||||
from cinder.tests.unit.scheduler import test_scheduler
|
||||
from cinder.volume import utils
|
||||
@ -114,7 +116,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
request_spec = {'volume_properties': {'project_id': 1,
|
||||
'size': 1},
|
||||
'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_id': ['fake-id1']}
|
||||
'volume_id': fake.VOLUME_ID}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
self.assertRaises(exception.NoValidHost, sched.schedule_create_volume,
|
||||
fake_context, request_spec, {})
|
||||
|
||||
@ -127,6 +130,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
request_spec = {'volume_properties': {'project_id': 1,
|
||||
'size': 1},
|
||||
'volume_type': {'name': 'LVM_iSCSI'}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
self.assertRaises(exception.NoValidHost,
|
||||
sched.schedule_create_volume,
|
||||
fake_context,
|
||||
@ -141,7 +145,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
# request_spec is missing 'volume_type'
|
||||
request_spec = {'volume_properties': {'project_id': 1,
|
||||
'size': 1},
|
||||
'volume_id': ['fake-id1']}
|
||||
'volume_id': fake.VOLUME_ID}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
self.assertRaises(exception.InvalidVolumeType,
|
||||
sched.schedule_create_volume,
|
||||
fake_context,
|
||||
@ -169,7 +174,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
request_spec = {'volume_properties': {'project_id': 1,
|
||||
'size': 1},
|
||||
'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_id': ['fake-id1']}
|
||||
'volume_id': fake.VOLUME_ID}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
self.assertRaises(exception.NoValidHost, sched.schedule_create_volume,
|
||||
fake_context, request_spec, {})
|
||||
self.assertTrue(self.was_admin)
|
||||
@ -188,6 +194,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
request_spec = {'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 1}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
weighed_host = sched._schedule(fake_context, request_spec, {})
|
||||
self.assertIsNotNone(weighed_host.obj)
|
||||
self.assertTrue(_mock_service_get_all.called)
|
||||
@ -205,6 +212,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
'size': 1},
|
||||
'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'CG_backend': 'host@lvmdriver'}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
weighed_host = sched._schedule(fake_context, request_spec, {})
|
||||
self.assertIsNone(weighed_host)
|
||||
|
||||
@ -220,6 +228,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
'size': 1},
|
||||
'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'CG_backend': 'host1'}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
weighed_host = sched._schedule(fake_context, request_spec, {})
|
||||
self.assertEqual('host1#lvm1', weighed_host.obj.host)
|
||||
|
||||
@ -243,6 +252,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
request_spec = {'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 1}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
filter_properties = {}
|
||||
|
||||
sched._schedule(self.context, request_spec,
|
||||
@ -259,6 +269,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
request_spec = {'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 1}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
filter_properties = {}
|
||||
|
||||
sched._schedule(self.context, request_spec,
|
||||
@ -275,6 +286,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
request_spec = {'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 1}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
|
||||
retry = dict(num_attempts=1)
|
||||
filter_properties = dict(retry=retry)
|
||||
@ -293,6 +305,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
request_spec = {'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 1}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
|
||||
retry = dict(num_attempts=2)
|
||||
filter_properties = dict(retry=retry)
|
||||
@ -343,10 +356,11 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
"""Do a successful pass through of with host_passes_filters()."""
|
||||
sched, ctx = self._host_passes_filters_setup(
|
||||
_mock_service_get_topic)
|
||||
request_spec = {'volume_id': 1,
|
||||
request_spec = {'volume_id': fake.VOLUME_ID,
|
||||
'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 1}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
ret_host = sched.host_passes_filters(ctx, 'host1#lvm1',
|
||||
request_spec, {})
|
||||
self.assertEqual('host1', utils.extract_host(ret_host.host))
|
||||
@ -358,10 +372,11 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
"""Do a successful pass through of with host_passes_filters()."""
|
||||
sched, ctx = self._host_passes_filters_setup(
|
||||
_mock_service_get_topic)
|
||||
request_spec = {'volume_id': 1,
|
||||
request_spec = {'volume_id': fake.VOLUME_ID,
|
||||
'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 1}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
ret_host = sched.host_passes_filters(ctx, 'host5#_pool0',
|
||||
request_spec, {})
|
||||
self.assertEqual('host5', utils.extract_host(ret_host.host))
|
||||
@ -372,10 +387,11 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
"""Fail the host due to insufficient capacity."""
|
||||
sched, ctx = self._host_passes_filters_setup(
|
||||
_mock_service_get_topic)
|
||||
request_spec = {'volume_id': 1,
|
||||
request_spec = {'volume_id': fake.VOLUME_ID,
|
||||
'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 1024}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
self.assertRaises(exception.NoValidHost,
|
||||
sched.host_passes_filters,
|
||||
ctx, 'host1#lvm1', request_spec, {})
|
||||
@ -390,12 +406,13 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
sched, ctx = self._host_passes_filters_setup(
|
||||
_mock_service_get_topic)
|
||||
extra_specs = {'volume_backend_name': 'lvm4'}
|
||||
request_spec = {'volume_id': 1,
|
||||
request_spec = {'volume_id': fake.VOLUME_ID,
|
||||
'volume_type': {'name': 'LVM_iSCSI',
|
||||
'extra_specs': extra_specs},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 200,
|
||||
'host': 'host4#lvm4'}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
host_state = sched.find_retype_host(ctx, request_spec,
|
||||
filter_properties={},
|
||||
migration_policy='never')
|
||||
@ -411,12 +428,13 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
sched, ctx = self._host_passes_filters_setup(
|
||||
_mock_service_get_topic)
|
||||
extra_specs = {'volume_backend_name': 'lvm3'}
|
||||
request_spec = {'volume_id': 1,
|
||||
request_spec = {'volume_id': fake.VOLUME_ID,
|
||||
'volume_type': {'name': 'LVM_iSCSI',
|
||||
'extra_specs': extra_specs},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 200,
|
||||
'host': 'host3#lvm3'}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
host_state = sched.find_retype_host(ctx, request_spec,
|
||||
filter_properties={},
|
||||
migration_policy='never')
|
||||
@ -429,12 +447,13 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
sched, ctx = self._host_passes_filters_setup(
|
||||
_mock_service_get_topic)
|
||||
extra_specs = {'volume_backend_name': 'lvm1'}
|
||||
request_spec = {'volume_id': 1,
|
||||
request_spec = {'volume_id': fake.VOLUME_ID,
|
||||
'volume_type': {'name': 'LVM_iSCSI',
|
||||
'extra_specs': extra_specs},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 200,
|
||||
'host': 'host4'}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
self.assertRaises(exception.NoValidHost, sched.find_retype_host, ctx,
|
||||
request_spec, filter_properties={},
|
||||
migration_policy='never')
|
||||
@ -446,12 +465,13 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
sched, ctx = self._host_passes_filters_setup(
|
||||
_mock_service_get_topic)
|
||||
extra_specs = {'volume_backend_name': 'lvm1'}
|
||||
request_spec = {'volume_id': 1,
|
||||
request_spec = {'volume_id': fake.VOLUME_ID,
|
||||
'volume_type': {'name': 'LVM_iSCSI',
|
||||
'extra_specs': extra_specs},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 200,
|
||||
'host': 'host4'}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
host_state = sched.find_retype_host(ctx, request_spec,
|
||||
filter_properties={},
|
||||
migration_policy='on-demand')
|
||||
@ -464,12 +484,13 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||
sched, ctx = self._host_passes_filters_setup(
|
||||
_mock_service_get_topic)
|
||||
extra_specs = {'volume_backend_name': 'lvm1'}
|
||||
request_spec = {'volume_id': 1,
|
||||
request_spec = {'volume_id': fake.VOLUME_ID,
|
||||
'volume_type': {'name': 'LVM_iSCSI',
|
||||
'extra_specs': extra_specs},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 2048,
|
||||
'host': 'host4'}}
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
self.assertRaises(exception.NoValidHost, sched.find_retype_host, ctx,
|
||||
request_spec, filter_properties={},
|
||||
migration_policy='on-demand')
|
||||
|
@ -86,7 +86,23 @@ class SchedulerRpcAPITestCase(test.TestCase):
|
||||
fanout=True,
|
||||
version='2.0')
|
||||
|
||||
def test_create_volume(self):
|
||||
@mock.patch('oslo_messaging.RPCClient.can_send_version', return_value=True)
|
||||
def test_create_volume(self, can_send_version):
|
||||
self._test_scheduler_api('create_volume',
|
||||
rpc_method='cast',
|
||||
topic='topic',
|
||||
volume_id='volume_id',
|
||||
snapshot_id='snapshot_id',
|
||||
image_id='image_id',
|
||||
request_spec='fake_request_spec',
|
||||
filter_properties='filter_properties',
|
||||
volume='volume',
|
||||
version='2.2')
|
||||
can_send_version.assert_has_calls([mock.call('2.2')])
|
||||
|
||||
@mock.patch('oslo_messaging.RPCClient.can_send_version',
|
||||
return_value=False)
|
||||
def test_create_volume_serialization(self, can_send_version):
|
||||
self._test_scheduler_api('create_volume',
|
||||
rpc_method='cast',
|
||||
topic='topic',
|
||||
@ -97,6 +113,7 @@ class SchedulerRpcAPITestCase(test.TestCase):
|
||||
filter_properties='filter_properties',
|
||||
volume='volume',
|
||||
version='2.0')
|
||||
can_send_version.assert_has_calls([mock.call('2.2')])
|
||||
|
||||
def test_migrate_volume_to_host(self):
|
||||
self._test_scheduler_api('migrate_volume_to_host',
|
||||
|
@ -130,7 +130,11 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
_mock_sched_create.side_effect = exception.NoValidHost(reason="")
|
||||
volume = fake_volume.fake_volume_obj(self.context)
|
||||
topic = 'fake_topic'
|
||||
request_spec = {'volume_id': volume.id}
|
||||
request_spec = {'volume_id': volume.id,
|
||||
'volume': {'id': volume.id, '_name_id': None,
|
||||
'metadata': {}, 'admin_metadata': {},
|
||||
'glance_metadata': {}}}
|
||||
request_spec_obj = objects.RequestSpec.from_primitives(request_spec)
|
||||
|
||||
self.manager.create_volume(self.context, topic, volume.id,
|
||||
request_spec=request_spec,
|
||||
@ -139,8 +143,8 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
_mock_volume_update.assert_called_once_with(self.context,
|
||||
volume.id,
|
||||
{'status': 'error'})
|
||||
_mock_sched_create.assert_called_once_with(self.context, request_spec,
|
||||
{})
|
||||
_mock_sched_create.assert_called_once_with(self.context,
|
||||
request_spec_obj, {})
|
||||
|
||||
_mock_message_create.assert_called_once_with(
|
||||
self.context, defined_messages.UNABLE_TO_ALLOCATE,
|
||||
@ -154,13 +158,14 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
topic = 'fake_topic'
|
||||
|
||||
request_spec = {'volume_id': volume.id}
|
||||
request_spec_obj = objects.RequestSpec.from_primitives(request_spec)
|
||||
|
||||
self.manager.create_volume(self.context, topic, volume.id,
|
||||
request_spec=request_spec,
|
||||
filter_properties={},
|
||||
volume=volume)
|
||||
_mock_sched_create.assert_called_once_with(self.context, request_spec,
|
||||
{})
|
||||
_mock_sched_create.assert_called_once_with(self.context,
|
||||
request_spec_obj, {})
|
||||
self.assertFalse(_mock_sleep.called)
|
||||
|
||||
@mock.patch('cinder.scheduler.driver.Scheduler.schedule_create_volume')
|
||||
@ -174,6 +179,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
topic = 'fake_topic'
|
||||
|
||||
request_spec = {'volume_id': volume.id}
|
||||
request_spec_obj = objects.RequestSpec.from_primitives(request_spec)
|
||||
|
||||
_mock_is_ready.side_effect = [False, False, True]
|
||||
|
||||
@ -181,8 +187,8 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
request_spec=request_spec,
|
||||
filter_properties={},
|
||||
volume=volume)
|
||||
_mock_sched_create.assert_called_once_with(self.context, request_spec,
|
||||
{})
|
||||
_mock_sched_create.assert_called_once_with(self.context,
|
||||
request_spec_obj, {})
|
||||
calls = [mock.call(1)] * 2
|
||||
_mock_sleep.assert_has_calls(calls)
|
||||
self.assertEqual(2, _mock_sleep.call_count)
|
||||
@ -198,6 +204,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
topic = 'fake_topic'
|
||||
|
||||
request_spec = {'volume_id': volume.id}
|
||||
request_spec_obj = objects.RequestSpec.from_primitives(request_spec)
|
||||
|
||||
_mock_is_ready.return_value = True
|
||||
|
||||
@ -205,8 +212,8 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
request_spec=request_spec,
|
||||
filter_properties={},
|
||||
volume=volume)
|
||||
_mock_sched_create.assert_called_once_with(self.context, request_spec,
|
||||
{})
|
||||
_mock_sched_create.assert_called_once_with(self.context,
|
||||
request_spec_obj, {})
|
||||
self.assertFalse(_mock_sleep.called)
|
||||
|
||||
@mock.patch('cinder.db.volume_get')
|
||||
|
@ -127,7 +127,7 @@ class FakeImageService(object):
|
||||
class BaseVolumeTestCase(test.TestCase):
|
||||
"""Test Case for volumes."""
|
||||
|
||||
FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa'
|
||||
FAKE_UUID = fake.IMAGE_ID
|
||||
|
||||
def setUp(self):
|
||||
super(BaseVolumeTestCase, self).setUp()
|
||||
@ -1082,12 +1082,14 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
|
||||
@mock.patch.object(keymgr, 'API', new=fake_keymgr.fake_api)
|
||||
def test_create_delete_volume_with_encrypted_volume_type(self):
|
||||
db_vol_type = db.volume_type_create(
|
||||
self.context, {'id': fake.VOLUME_TYPE_ID, 'name': 'LUKS'})
|
||||
db.volume_type_create(self.context,
|
||||
{'id': fake.VOLUME_TYPE_ID, 'name': 'LUKS'})
|
||||
db.volume_type_encryption_create(
|
||||
self.context, fake.VOLUME_TYPE_ID,
|
||||
{'control_location': 'front-end', 'provider': ENCRYPTION_PROVIDER})
|
||||
|
||||
db_vol_type = db.volume_type_get_by_name(self.context, 'LUKS')
|
||||
|
||||
volume = self.volume_api.create(self.context,
|
||||
1,
|
||||
'name',
|
||||
@ -3671,7 +3673,7 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
volume_api = cinder.volume.api.API(
|
||||
image_service=FakeImageService())
|
||||
volume = volume_api.create(self.context, 2, 'name', 'description',
|
||||
image_id=1)
|
||||
image_id=self.FAKE_UUID)
|
||||
volume_id = volume['id']
|
||||
self.assertEqual('creating', volume['status'])
|
||||
|
||||
|
@ -245,7 +245,8 @@ class VolumeRpcAPITestCase(test.TestCase):
|
||||
self._test_volume_api('delete_cgsnapshot', rpc_method='cast',
|
||||
cgsnapshot=self.fake_cgsnap, version='2.0')
|
||||
|
||||
def test_create_volume(self):
|
||||
@mock.patch('oslo_messaging.RPCClient.can_send_version', return_value=True)
|
||||
def test_create_volume(self, can_send_version):
|
||||
self._test_volume_api('create_volume',
|
||||
rpc_method='cast',
|
||||
volume=self.fake_volume_obj,
|
||||
@ -253,9 +254,12 @@ class VolumeRpcAPITestCase(test.TestCase):
|
||||
request_spec='fake_request_spec',
|
||||
filter_properties='fake_properties',
|
||||
allow_reschedule=True,
|
||||
version='2.0')
|
||||
version='2.4')
|
||||
can_send_version.assert_has_calls([mock.call('2.4')])
|
||||
|
||||
def test_create_volume_serialization(self):
|
||||
@mock.patch('oslo_messaging.RPCClient.can_send_version',
|
||||
return_value=False)
|
||||
def test_create_volume_serialization(self, can_send_version):
|
||||
request_spec = {"metadata": self.fake_volume_metadata}
|
||||
self._test_volume_api('create_volume',
|
||||
rpc_method='cast',
|
||||
@ -265,6 +269,7 @@ class VolumeRpcAPITestCase(test.TestCase):
|
||||
filter_properties='fake_properties',
|
||||
allow_reschedule=True,
|
||||
version='2.0')
|
||||
can_send_version.assert_has_calls([mock.call('2.4')])
|
||||
|
||||
def test_delete_volume(self):
|
||||
self._test_volume_api('delete_volume',
|
||||
|
@ -506,6 +506,16 @@ class EntryCreateTask(flow_utils.CinderTask):
|
||||
volume = objects.Volume(context=context, **volume_properties)
|
||||
volume.create()
|
||||
|
||||
# FIXME(dulek): We're passing this volume_properties dict through RPC
|
||||
# in request_spec. This shouldn't be needed, most data is replicated
|
||||
# in both volume and other places. We should make Newton read data
|
||||
# from just one correct place and leave just compatibility code.
|
||||
#
|
||||
# Right now - let's move it to versioned objects to be able to make
|
||||
# non-backward compatible changes.
|
||||
|
||||
volume_properties = objects.VolumeProperties(**volume_properties)
|
||||
|
||||
return {
|
||||
'volume_id': volume['id'],
|
||||
'volume_properties': volume_properties,
|
||||
@ -687,10 +697,6 @@ class VolumeCastTask(flow_utils.CinderTask):
|
||||
# If cgroup_id existed, we should cast volume to the scheduler
|
||||
# to choose a proper pool whose backend is same as CG's backend.
|
||||
cgroup = objects.ConsistencyGroup.get_by_id(context, cgroup_id)
|
||||
# FIXME(wanghao): CG_backend got added before request_spec was
|
||||
# converted to versioned objects. We should make sure that this
|
||||
# will be handled by object version translations once we add
|
||||
# RequestSpec object.
|
||||
request_spec['CG_backend'] = vol_utils.extract_host(cgroup.host)
|
||||
elif snapshot_id and CONF.snapshot_same_host:
|
||||
# NOTE(Rongze Zhu): A simple solution for bug 1008866.
|
||||
@ -741,7 +747,13 @@ class VolumeCastTask(flow_utils.CinderTask):
|
||||
|
||||
def execute(self, context, **kwargs):
|
||||
scheduler_hints = kwargs.pop('scheduler_hints', None)
|
||||
request_spec = kwargs.copy()
|
||||
db_vt = kwargs.pop('volume_type')
|
||||
kwargs['volume_type'] = None
|
||||
if db_vt:
|
||||
kwargs['volume_type'] = objects.VolumeType()
|
||||
objects.VolumeType()._from_db_object(context,
|
||||
kwargs['volume_type'], db_vt)
|
||||
request_spec = objects.RequestSpec(**kwargs)
|
||||
filter_properties = {}
|
||||
if scheduler_hints:
|
||||
filter_properties['scheduler_hints'] = scheduler_hints
|
||||
|
@ -528,12 +528,17 @@ class VolumeManager(manager.SchedulerDependentManager):
|
||||
# by its volume_id.
|
||||
volume = objects.Volume.get_by_id(context, volume_id)
|
||||
|
||||
# FIXME(dulek): Remove this in v3.0 of RPC API.
|
||||
if isinstance(request_spec, dict):
|
||||
# We may receive request_spec as dict from older clients.
|
||||
request_spec = objects.RequestSpec.from_primitives(request_spec)
|
||||
|
||||
context_elevated = context.elevated()
|
||||
if filter_properties is None:
|
||||
filter_properties = {}
|
||||
|
||||
if request_spec is None:
|
||||
request_spec = {}
|
||||
request_spec = objects.RequestSpec()
|
||||
|
||||
try:
|
||||
# NOTE(flaper87): Driver initialization is
|
||||
|
@ -103,9 +103,10 @@ class VolumeAPI(rpc.RPCAPI):
|
||||
2.2 - Adds support for sending objects over RPC in manage_existing().
|
||||
2.3 - Adds support for sending objects over RPC in
|
||||
initialize_connection().
|
||||
2.4 - Sends request_spec as object in create_volume().
|
||||
"""
|
||||
|
||||
RPC_API_VERSION = '2.3'
|
||||
RPC_API_VERSION = '2.4'
|
||||
TOPIC = CONF.volume_topic
|
||||
BINARY = 'cinder-volume'
|
||||
|
||||
@ -156,12 +157,19 @@ class VolumeAPI(rpc.RPCAPI):
|
||||
|
||||
def create_volume(self, ctxt, volume, host, request_spec,
|
||||
filter_properties, allow_reschedule=True):
|
||||
request_spec_p = jsonutils.to_primitive(request_spec)
|
||||
cctxt = self._get_cctxt(host, '2.0')
|
||||
cctxt.cast(ctxt, 'create_volume', volume_id=volume.id,
|
||||
request_spec=request_spec_p,
|
||||
filter_properties=filter_properties,
|
||||
allow_reschedule=allow_reschedule, volume=volume)
|
||||
msg_args = {'volume_id': volume.id, 'request_spec': request_spec,
|
||||
'filter_properties': filter_properties,
|
||||
'allow_reschedule': allow_reschedule,
|
||||
'volume': volume,
|
||||
}
|
||||
version = '2.4'
|
||||
if not self.client.can_send_version('2.4'):
|
||||
# Send request_spec as dict
|
||||
version = '2.0'
|
||||
msg_args['request_spec'] = jsonutils.to_primitive(request_spec)
|
||||
|
||||
cctxt = self._get_cctxt(host, version)
|
||||
cctxt.cast(ctxt, 'create_volume', **msg_args)
|
||||
|
||||
def delete_volume(self, ctxt, volume, unmanage_only=False, cascade=False):
|
||||
cctxt = self._get_cctxt(volume.host, '2.0')
|
||||
|
@ -89,12 +89,14 @@ objects_ignore_messages = [
|
||||
"Module 'cinder.objects' has no 'ConsistencyGroupList' member",
|
||||
"Module 'cinder.objects' has no 'QualityOfServiceSpecs' member",
|
||||
"Module 'cinder.objects' has no 'QualityOfServiceSpecsList' member",
|
||||
"Module 'cinder.objects' has no 'RequestSpec' member",
|
||||
"Module 'cinder.objects' has no 'Service' member",
|
||||
"Module 'cinder.objects' has no 'ServiceList' member",
|
||||
"Module 'cinder.objects' has no 'Snapshot' member",
|
||||
"Module 'cinder.objects' has no 'SnapshotList' member",
|
||||
"Module 'cinder.objects' has no 'Volume' member",
|
||||
"Module 'cinder.objects' has no 'VolumeList' member",
|
||||
"Module 'cinder.objects' has no 'VolumeProperties' member",
|
||||
"Module 'cinder.objects' has no 'VolumeType' member",
|
||||
"Module 'cinder.objects' has no 'VolumeTypeList' member",
|
||||
]
|
||||
|
Loading…
x
Reference in New Issue
Block a user