Merge "Route extend_volume calls to scheduler"
This commit is contained in:
commit
0f1a2202c9
@ -39,7 +39,23 @@ class CapacityFilter(filters.BaseHostFilter):
|
||||
if host_state.host == filter_properties.get('vol_exists_on'):
|
||||
return True
|
||||
|
||||
volume_size = filter_properties.get('size')
|
||||
spec = filter_properties.get('request_spec')
|
||||
if spec:
|
||||
volid = spec.get('volume_id')
|
||||
|
||||
if filter_properties.get('new_size'):
|
||||
# If new_size is passed, we are allocating space to extend a volume
|
||||
requested_size = (int(filter_properties.get('new_size')) -
|
||||
int(filter_properties.get('size')))
|
||||
LOG.debug('Checking if host %(host)s can extend the volume %(id)s'
|
||||
'in %(size)s GB', {'host': host_state.host, 'id': volid,
|
||||
'size': requested_size})
|
||||
else:
|
||||
requested_size = filter_properties.get('size')
|
||||
LOG.debug('Checking if host %(host)s can create a %(size)s GB '
|
||||
'volume (%(id)s)',
|
||||
{'host': host_state.host, 'id': volid,
|
||||
'size': requested_size})
|
||||
|
||||
if host_state.free_capacity_gb is None:
|
||||
# Fail Safe
|
||||
@ -78,7 +94,7 @@ class CapacityFilter(filters.BaseHostFilter):
|
||||
free = free_space - math.floor(total * reserved)
|
||||
|
||||
msg_args = {"host": host_state.host,
|
||||
"requested": volume_size,
|
||||
"requested": requested_size,
|
||||
"available": free}
|
||||
|
||||
# NOTE(xyang): If 'provisioning:type' is 'thick' in extra_specs,
|
||||
@ -99,7 +115,7 @@ class CapacityFilter(filters.BaseHostFilter):
|
||||
if (thin and host_state.thin_provisioning_support and
|
||||
host_state.max_over_subscription_ratio >= 1):
|
||||
provisioned_ratio = ((host_state.provisioned_capacity_gb +
|
||||
volume_size) / total)
|
||||
requested_size) / total)
|
||||
if provisioned_ratio > host_state.max_over_subscription_ratio:
|
||||
LOG.warning(_LW(
|
||||
"Insufficient free space for thin provisioning. "
|
||||
@ -120,7 +136,7 @@ class CapacityFilter(filters.BaseHostFilter):
|
||||
# of reserved space) which we can over-subscribe.
|
||||
adjusted_free_virtual = (
|
||||
free * host_state.max_over_subscription_ratio)
|
||||
return adjusted_free_virtual >= volume_size
|
||||
return adjusted_free_virtual >= requested_size
|
||||
elif thin and host_state.thin_provisioning_support:
|
||||
LOG.warning(_LW("Filtering out host %(host)s with an invalid "
|
||||
"maximum over subscription ratio of "
|
||||
@ -131,7 +147,7 @@ class CapacityFilter(filters.BaseHostFilter):
|
||||
"host": host_state.host})
|
||||
return False
|
||||
|
||||
if free < volume_size:
|
||||
if free < requested_size:
|
||||
LOG.warning(_LW("Insufficient free space for volume creation "
|
||||
"on host %(host)s (requested / avail): "
|
||||
"%(requested)s/%(available)s"), msg_args)
|
||||
|
@ -293,6 +293,28 @@ class SchedulerManager(manager.Manager):
|
||||
"""
|
||||
return self.driver.get_pools(context, filters)
|
||||
|
||||
def extend_volume(self, context, volume, new_size, reservations,
|
||||
request_spec=None, filter_properties=None):
|
||||
|
||||
def _extend_volume_set_error(self, context, ex, request_spec):
|
||||
volume_state = {'volume_state': {'status': 'available'}}
|
||||
self._set_volume_state_and_notify('extend_volume', volume_state,
|
||||
context, ex, request_spec)
|
||||
|
||||
if not filter_properties:
|
||||
filter_properties = {}
|
||||
|
||||
filter_properties['new_size'] = new_size
|
||||
try:
|
||||
self.driver.host_passes_filters(context, volume.host,
|
||||
request_spec, filter_properties)
|
||||
volume_rpcapi.VolumeAPI().extend_volume(context, volume, new_size,
|
||||
reservations)
|
||||
except exception.NoValidHost as ex:
|
||||
QUOTAS.rollback(context, reservations,
|
||||
project_id=volume.project_id)
|
||||
_extend_volume_set_error(self, context, ex, request_spec)
|
||||
|
||||
def _set_volume_state_and_notify(self, method, updates, context, ex,
|
||||
request_spec, msg=None):
|
||||
# TODO(harlowja): move into a task that just does this later.
|
||||
|
@ -61,9 +61,10 @@ class SchedulerAPI(rpc.RPCAPI):
|
||||
|
||||
3.0 - Remove 2.x compatibility
|
||||
3.1 - Adds notify_service_capabilities()
|
||||
3.2 - Adds extend_volume()
|
||||
"""
|
||||
|
||||
RPC_API_VERSION = '3.1'
|
||||
RPC_API_VERSION = '3.2'
|
||||
RPC_DEFAULT_VERSION = '3.0'
|
||||
TOPIC = constants.SCHEDULER_TOPIC
|
||||
BINARY = 'cinder-scheduler'
|
||||
@ -132,6 +133,25 @@ class SchedulerAPI(rpc.RPCAPI):
|
||||
}
|
||||
return cctxt.cast(ctxt, 'manage_existing', **msg_args)
|
||||
|
||||
def extend_volume(self, ctxt, volume, new_size, reservations,
|
||||
request_spec, filter_properties=None):
|
||||
cctxt = self._get_cctxt()
|
||||
if not cctxt.can_send_version('3.2'):
|
||||
msg = _('extend_volume requires cinder-scheduler '
|
||||
'RPC API version >= 3.2.')
|
||||
raise exception.ServiceTooOld(msg)
|
||||
|
||||
request_spec_p = jsonutils.to_primitive(request_spec)
|
||||
msg_args = {
|
||||
'volume': volume,
|
||||
'new_size': new_size,
|
||||
'reservations': reservations,
|
||||
'request_spec': request_spec_p,
|
||||
'filter_properties': filter_properties,
|
||||
}
|
||||
|
||||
return cctxt.cast(ctxt, 'extend_volume', **msg_args)
|
||||
|
||||
def get_pools(self, ctxt, filters=None):
|
||||
cctxt = self._get_cctxt()
|
||||
return cctxt.call(ctxt, 'get_pools', filters=filters)
|
||||
|
@ -61,7 +61,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
def test_filter_passes(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100}
|
||||
filter_properties = {'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -73,7 +74,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
def test_filter_current_host_passes(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100, 'vol_exists_on': 'host1'}
|
||||
filter_properties = {'size': 100, 'vol_exists_on': 'host1',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 100,
|
||||
@ -85,7 +87,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
def test_filter_fails(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100}
|
||||
filter_properties = {'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 200,
|
||||
@ -98,7 +101,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
def test_filter_fails_free_capacity_None(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100}
|
||||
filter_properties = {'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_capacity_gb': None,
|
||||
@ -109,7 +113,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
def test_filter_passes_infinite(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100}
|
||||
filter_properties = {'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_capacity_gb': 'infinite',
|
||||
@ -117,10 +122,37 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'service': service})
|
||||
self.assertTrue(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
def test_filter_extend_request(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'new_size': 100, 'size': 50,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_capacity_gb': 200,
|
||||
'updated_at': None,
|
||||
'total_capacity_gb': 500,
|
||||
'service': service})
|
||||
self.assertTrue(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
def test_filter_extend_request_negative(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 50,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_capacity_gb': 49,
|
||||
'updated_at': None,
|
||||
'total_capacity_gb': 500,
|
||||
'service': service})
|
||||
self.assertFalse(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
def test_filter_passes_unknown(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100}
|
||||
filter_properties = {'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_capacity_gb': 'unknown',
|
||||
@ -131,7 +163,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
def test_filter_passes_total_infinite(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100}
|
||||
filter_properties = {'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_capacity_gb': 'infinite',
|
||||
@ -144,7 +177,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
def test_filter_passes_total_unknown(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100}
|
||||
filter_properties = {'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_capacity_gb': 'unknown',
|
||||
@ -157,7 +191,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
def test_filter_fails_total_infinite(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100}
|
||||
filter_properties = {'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 'infinite',
|
||||
@ -169,7 +204,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
def test_filter_fails_total_unknown(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100}
|
||||
filter_properties = {'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 'unknown',
|
||||
@ -181,7 +217,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
def test_filter_fails_total_zero(self, _mock_serv_is_up):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100}
|
||||
filter_properties = {'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 0,
|
||||
@ -197,7 +234,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> False'}
|
||||
'<is> False',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -218,7 +256,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> False'}
|
||||
'<is> False',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -239,7 +278,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> False',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> True'}
|
||||
'<is> True',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
# If "thin_provisioning_support" is False,
|
||||
# "max_over_subscription_ratio" will be ignored.
|
||||
@ -262,7 +302,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> False'}
|
||||
'<is> False',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -283,7 +324,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> False'}
|
||||
'<is> False',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -304,7 +346,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> False'}
|
||||
'<is> False',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -325,7 +368,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> False'}
|
||||
'<is> False',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -346,7 +390,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> False'}
|
||||
'<is> False',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -367,7 +412,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> False',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> True'}
|
||||
'<is> True',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
# If "thin_provisioning_support" is False,
|
||||
# "max_over_subscription_ratio" will be ignored.
|
||||
@ -390,7 +436,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> True'}
|
||||
'<is> True',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -411,7 +458,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> True'}
|
||||
'<is> True',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -432,7 +480,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> False'}
|
||||
'<is> False',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -453,7 +502,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> True'}
|
||||
'<is> True',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -474,7 +524,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
'capabilities:thin_provisioning_support':
|
||||
'<is> True',
|
||||
'capabilities:thick_provisioning_support':
|
||||
'<is> True'}
|
||||
'<is> True',
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -500,7 +551,8 @@ class CapacityFilterTestCase(HostFiltersTestCase):
|
||||
_mock_serv_is_up.return_value = True
|
||||
filt_cls = self.class_map['CapacityFilter']()
|
||||
filter_properties = {'size': 100,
|
||||
'volume_type': volume_type}
|
||||
'volume_type': volume_type,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
service = {'disabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'total_capacity_gb': 500,
|
||||
@ -530,8 +582,8 @@ class AffinityFilterTestCase(HostFiltersTestCase):
|
||||
vol_id = volume.id
|
||||
|
||||
filter_properties = {'context': self.context.elevated(),
|
||||
'scheduler_hints': {
|
||||
'different_host': [vol_id], }}
|
||||
'scheduler_hints': {'different_host': [vol_id], },
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
|
||||
self.assertTrue(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
@ -550,8 +602,8 @@ class AffinityFilterTestCase(HostFiltersTestCase):
|
||||
vol_id = volume.id
|
||||
|
||||
filter_properties = {'context': self.context.elevated(),
|
||||
'scheduler_hints': {
|
||||
'different_host': [vol_id], }}
|
||||
'scheduler_hints': {'different_host': [vol_id], },
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
|
||||
self.assertTrue(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
@ -574,8 +626,8 @@ class AffinityFilterTestCase(HostFiltersTestCase):
|
||||
vol_id = volume.id
|
||||
|
||||
filter_properties = {'context': self.context.elevated(),
|
||||
'scheduler_hints': {
|
||||
'different_host': [vol_id], }}
|
||||
'scheduler_hints': {'different_host': [vol_id], },
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
|
||||
self.assertFalse(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
@ -584,7 +636,8 @@ class AffinityFilterTestCase(HostFiltersTestCase):
|
||||
host = fakes.FakeHostState('host1', {})
|
||||
|
||||
filter_properties = {'context': self.context.elevated(),
|
||||
'scheduler_hints': None}
|
||||
'scheduler_hints': None,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
|
||||
self.assertTrue(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
@ -944,7 +997,8 @@ class InstanceLocalityFilterTestCase(HostFiltersTestCase):
|
||||
uuid = nova.novaclient().servers.create('host1')
|
||||
|
||||
filter_properties = {'context': self.context,
|
||||
'scheduler_hints': {'local_to_instance': uuid}}
|
||||
'scheduler_hints': {'local_to_instance': uuid},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
self.assertTrue(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
@mock.patch('novaclient.client.discover_extensions')
|
||||
@ -958,7 +1012,8 @@ class InstanceLocalityFilterTestCase(HostFiltersTestCase):
|
||||
uuid = nova.novaclient().servers.create('host2')
|
||||
|
||||
filter_properties = {'context': self.context,
|
||||
'scheduler_hints': {'local_to_instance': uuid}}
|
||||
'scheduler_hints': {'local_to_instance': uuid},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
self.assertFalse(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
def test_handles_none(self):
|
||||
@ -966,7 +1021,8 @@ class InstanceLocalityFilterTestCase(HostFiltersTestCase):
|
||||
host = fakes.FakeHostState('host1', {})
|
||||
|
||||
filter_properties = {'context': self.context,
|
||||
'scheduler_hints': None}
|
||||
'scheduler_hints': None,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
self.assertTrue(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
def test_invalid_uuid(self):
|
||||
@ -975,7 +1031,8 @@ class InstanceLocalityFilterTestCase(HostFiltersTestCase):
|
||||
|
||||
filter_properties = {'context': self.context,
|
||||
'scheduler_hints':
|
||||
{'local_to_instance': 'e29b11d4-not-valid-a716'}}
|
||||
{'local_to_instance': 'e29b11d4-not-valid-a716'},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
self.assertRaises(exception.InvalidUUID,
|
||||
filt_cls.host_passes, host, filter_properties)
|
||||
|
||||
@ -988,7 +1045,8 @@ class InstanceLocalityFilterTestCase(HostFiltersTestCase):
|
||||
uuid = nova.novaclient().servers.create('host1')
|
||||
|
||||
filter_properties = {'context': self.context,
|
||||
'scheduler_hints': {'local_to_instance': uuid}}
|
||||
'scheduler_hints': {'local_to_instance': uuid},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
self.assertRaises(exception.CinderException,
|
||||
filt_cls.host_passes, host, filter_properties)
|
||||
|
||||
@ -1000,7 +1058,8 @@ class InstanceLocalityFilterTestCase(HostFiltersTestCase):
|
||||
filt_cls = self.class_map['InstanceLocalityFilter']()
|
||||
host = fakes.FakeHostState('host1', {})
|
||||
|
||||
filter_properties = {'context': self.context, 'size': 100}
|
||||
filter_properties = {'context': self.context, 'size': 100,
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
self.assertTrue(filt_cls.host_passes(host, filter_properties))
|
||||
|
||||
@mock.patch('cinder.compute.nova.novaclient')
|
||||
@ -1014,7 +1073,8 @@ class InstanceLocalityFilterTestCase(HostFiltersTestCase):
|
||||
|
||||
filter_properties = \
|
||||
{'context': self.context, 'scheduler_hints':
|
||||
{'local_to_instance': 'e29b11d4-15ef-34a9-a716-598a6f0b5467'}}
|
||||
{'local_to_instance': 'e29b11d4-15ef-34a9-a716-598a6f0b5467'},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
self.assertRaises(exception.APITimeout,
|
||||
filt_cls.host_passes, host, filter_properties)
|
||||
|
||||
@ -1279,7 +1339,8 @@ class BasicFiltersTestCase(HostFiltersTestCase):
|
||||
capabilities.update(ecaps)
|
||||
service = {'disabled': False}
|
||||
filter_properties = {'resource_type': {'name': 'fake_type',
|
||||
'extra_specs': especs}}
|
||||
'extra_specs': especs},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_capacity_gb': 1024,
|
||||
'capabilities': capabilities,
|
||||
@ -1436,7 +1497,8 @@ class BasicFiltersTestCase(HostFiltersTestCase):
|
||||
filter_properties = {'resource_type': {'memory_mb': 1024,
|
||||
'root_gb': 200,
|
||||
'ephemeral_gb': 0},
|
||||
'scheduler_hints': {'query': self.json_query}}
|
||||
'scheduler_hints': {'query': self.json_query},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
capabilities = {'enabled': True}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_ram_mb': 1024,
|
||||
@ -1448,7 +1510,8 @@ class BasicFiltersTestCase(HostFiltersTestCase):
|
||||
filt_cls = self.class_map['JsonFilter']()
|
||||
filter_properties = {'resource_type': {'memory_mb': 1024,
|
||||
'root_gb': 200,
|
||||
'ephemeral_gb': 0}}
|
||||
'ephemeral_gb': 0},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
capabilities = {'enabled': True}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_ram_mb': 0,
|
||||
@ -1461,7 +1524,8 @@ class BasicFiltersTestCase(HostFiltersTestCase):
|
||||
filter_properties = {'resource_type': {'memory_mb': 1024,
|
||||
'root_gb': 200,
|
||||
'ephemeral_gb': 0},
|
||||
'scheduler_hints': {'query': self.json_query}}
|
||||
'scheduler_hints': {'query': self.json_query},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
capabilities = {'enabled': True}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_ram_mb': 1023,
|
||||
@ -1474,7 +1538,8 @@ class BasicFiltersTestCase(HostFiltersTestCase):
|
||||
filter_properties = {'resource_type': {'memory_mb': 1024,
|
||||
'root_gb': 200,
|
||||
'ephemeral_gb': 0},
|
||||
'scheduler_hints': {'query': self.json_query}}
|
||||
'scheduler_hints': {'query': self.json_query},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
capabilities = {'enabled': True}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_ram_mb': 1024,
|
||||
@ -1491,7 +1556,8 @@ class BasicFiltersTestCase(HostFiltersTestCase):
|
||||
filter_properties = {'resource_type': {'memory_mb': 1024,
|
||||
'root_gb': 200,
|
||||
'ephemeral_gb': 0},
|
||||
'scheduler_hints': {'query': json_query}}
|
||||
'scheduler_hints': {'query': json_query},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
capabilities = {'enabled': False}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_ram_mb': 1024,
|
||||
@ -1507,7 +1573,8 @@ class BasicFiltersTestCase(HostFiltersTestCase):
|
||||
['not', '$service.disabled']])
|
||||
filter_properties = {'resource_type': {'memory_mb': 1024,
|
||||
'local_gb': 200},
|
||||
'scheduler_hints': {'query': json_query}}
|
||||
'scheduler_hints': {'query': json_query},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}}
|
||||
capabilities = {'enabled': True}
|
||||
host = fakes.FakeHostState('host1',
|
||||
{'free_ram_mb': 1024,
|
||||
@ -1532,6 +1599,7 @@ class BasicFiltersTestCase(HostFiltersTestCase):
|
||||
'scheduler_hints': {
|
||||
'query': jsonutils.dumps(raw),
|
||||
},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}
|
||||
}
|
||||
|
||||
# Passes
|
||||
@ -1633,6 +1701,7 @@ class BasicFiltersTestCase(HostFiltersTestCase):
|
||||
'scheduler_hints': {
|
||||
'query': jsonutils.dumps(raw),
|
||||
},
|
||||
'request_spec': {'volume_id': fake.VOLUME_ID}
|
||||
}
|
||||
self.assertEqual(expected,
|
||||
filt_cls.host_passes(host, filter_properties))
|
||||
|
@ -170,6 +170,37 @@ class SchedulerRpcAPITestCase(test.TestCase):
|
||||
version='3.0')
|
||||
create_worker_mock.assert_not_called()
|
||||
|
||||
@mock.patch('oslo_messaging.RPCClient.can_send_version',
|
||||
return_value=False)
|
||||
def test_extend_volume_capped(self, can_send_version_mock):
|
||||
new_size = 4
|
||||
volume = fake_volume.fake_volume_obj(self.context)
|
||||
self.assertRaises(exception.ServiceTooOld,
|
||||
self._test_scheduler_api,
|
||||
'extend_volume',
|
||||
rpc_method='cast',
|
||||
request_spec='fake_request_spec',
|
||||
filter_properties='filter_properties',
|
||||
volume=volume,
|
||||
new_size=new_size,
|
||||
reservations=['RESERVATIONS'],
|
||||
version='3.0')
|
||||
|
||||
@mock.patch('oslo_messaging.RPCClient.can_send_version', return_value=True)
|
||||
def test_extend_volume(self, can_send_version_mock):
|
||||
new_size = 4
|
||||
volume = fake_volume.fake_volume_obj(self.context)
|
||||
create_worker_mock = self.mock_object(volume, 'create_worker')
|
||||
self._test_scheduler_api('extend_volume',
|
||||
rpc_method='cast',
|
||||
request_spec='fake_request_spec',
|
||||
filter_properties='filter_properties',
|
||||
volume=volume,
|
||||
new_size=new_size,
|
||||
reservations=['RESERVATIONS'],
|
||||
version='3.0')
|
||||
create_worker_mock.assert_not_called()
|
||||
|
||||
def test_get_pools(self):
|
||||
self._test_scheduler_api('get_pools',
|
||||
rpc_method='call',
|
||||
|
@ -1071,7 +1071,6 @@ class VolumeTestCase(base.BaseVolumeTestCase):
|
||||
def test_get_all_limit_bad_value(self):
|
||||
"""Test value of 'limit' is numeric and >= 0"""
|
||||
volume_api = cinder.volume.api.API()
|
||||
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
volume_api.get_all,
|
||||
self.context,
|
||||
@ -3956,6 +3955,36 @@ class VolumeTestCase(base.BaseVolumeTestCase):
|
||||
volume_api.extend, self.context,
|
||||
volume, 3)
|
||||
|
||||
# Test scheduler path
|
||||
limit_check.side_effect = None
|
||||
reserve.side_effect = None
|
||||
db.volume_update(self.context, volume.id, {'status': 'available'})
|
||||
volume_api.scheduler_rpcapi = mock.MagicMock()
|
||||
volume_api.scheduler_rpcapi.extend_volume = mock.MagicMock()
|
||||
|
||||
volume_api.extend(self.context, volume, 3)
|
||||
|
||||
request_spec = {
|
||||
'volume_properties': volume,
|
||||
'volume_type': {},
|
||||
'volume_id': volume.id
|
||||
}
|
||||
volume_api.scheduler_rpcapi.extend_volume.assert_called_once_with(
|
||||
self.context, volume, 3, ["RESERVATION"], request_spec)
|
||||
|
||||
# Test direct volume path
|
||||
limit_check.side_effect = None
|
||||
reserve.side_effect = None
|
||||
db.volume_update(self.context, volume.id, {'status': 'available'})
|
||||
ext_mock = mock.MagicMock(side_effect=exception.ServiceTooOld)
|
||||
volume_api.volume_rpcapi.extend_volume = mock.MagicMock()
|
||||
volume_api.scheduler_rpcapi.extend_volume = ext_mock
|
||||
|
||||
volume_api.extend(self.context, volume, 3)
|
||||
|
||||
volume_api.volume_rpcapi.extend_volume.assert_called_once_with(
|
||||
self.context, volume, 3, ["RESERVATION"])
|
||||
|
||||
# clean up
|
||||
self.volume.delete_volume(self.context, volume)
|
||||
|
||||
|
@ -1314,8 +1314,32 @@ class API(base.Base):
|
||||
if reservations is None:
|
||||
_roll_back_status()
|
||||
|
||||
self.volume_rpcapi.extend_volume(context, volume, new_size,
|
||||
reservations)
|
||||
volume_type = {}
|
||||
if volume.volume_type_id:
|
||||
volume_type = volume_types.get_volume_type(context.elevated(),
|
||||
volume.volume_type_id)
|
||||
|
||||
request_spec = {
|
||||
'volume_properties': volume,
|
||||
'volume_type': volume_type,
|
||||
'volume_id': volume.id
|
||||
}
|
||||
|
||||
try:
|
||||
self.scheduler_rpcapi.extend_volume(context, volume, new_size,
|
||||
reservations, request_spec)
|
||||
except exception.ServiceTooOld as e:
|
||||
# NOTE(erlon): During rolling upgrades scheduler and volume can
|
||||
# have different versions. This check makes sure that a new
|
||||
# version of the volume service won't break.
|
||||
msg = _LW("Failed to send extend volume request to scheduler. "
|
||||
"Falling back to old behaviour. This is normal during a "
|
||||
"live-upgrade. Error: %(e)s")
|
||||
LOG.warning(msg, {'e': e})
|
||||
# TODO(erlon): Remove in Pike
|
||||
self.volume_rpcapi.extend_volume(context, volume, new_size,
|
||||
reservations)
|
||||
|
||||
LOG.info(_LI("Extend volume request issued successfully."),
|
||||
resource=volume)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user