Merge "Extend share will go through scheduler"

This commit is contained in:
Zuul 2021-08-29 18:38:39 +00:00 committed by Gerrit Code Review
commit 8e066548a4
15 changed files with 167 additions and 18 deletions

View File

@ -2435,6 +2435,15 @@ share_force_delete:
in: body
required: true
type: string
share_force_extend:
description: |
(Admin only). Defines whether to go through scheduler, Set to `True` will
extend share directly. Set to `False` will go through scheduler, default
is `False`.
in: body
required: false
type: boolean
min_version: 2.64
share_group_host:
description: |
The share group host name.

View File

@ -1,5 +1,6 @@
{
"extend": {
"new_size": 2
"new_size": 2,
"force": "true"
}
}

View File

@ -351,6 +351,7 @@ Request
- share_id: share_id
- extend: extend
- new_size: share_new_size
- force: share_force_extend
Request example

View File

@ -353,3 +353,8 @@ user documentation.
endpoint: 'update_security_service', 'update_security_service_check' and
'add_security_service_check'.
2.64
----
Added 'force' field to extend share api, which can extend share directly
without go through share scheduler.

View File

@ -515,11 +515,11 @@ class ShareMixin(object):
def _extend(self, req, id, body):
"""Extend size of a share."""
context = req.environ['manila.context']
share, size = self._get_valid_resize_parameters(
share, size, force = self._get_valid_extend_parameters(
context, id, body, 'os-extend')
try:
self.share_api.extend(context, share, size)
self.share_api.extend(context, share, size, force=force)
except (exception.InvalidInput, exception.InvalidShare) as e:
raise webob.exc.HTTPBadRequest(explanation=six.text_type(e))
except exception.ShareSizeExceedsAvailableQuota as e:
@ -530,7 +530,7 @@ class ShareMixin(object):
def _shrink(self, req, id, body):
"""Shrink size of a share."""
context = req.environ['manila.context']
share, size = self._get_valid_resize_parameters(
share, size = self._get_valid_shrink_parameters(
context, id, body, 'os-shrink')
try:
@ -540,15 +540,40 @@ class ShareMixin(object):
return webob.Response(status_int=http_client.ACCEPTED)
def _get_valid_resize_parameters(self, context, id, body, action):
def _get_valid_extend_parameters(self, context, id, body, action):
try:
share = self.share_api.get(context, id)
except exception.NotFound as e:
raise webob.exc.HTTPNotFound(explanation=six.text_type(e))
try:
size = int(body.get(action,
body.get(action.split('os-')[-1]))['new_size'])
size = int(body.get(action, body.get('extend'))['new_size'])
except (KeyError, ValueError, TypeError):
msg = _("New share size must be specified as an integer.")
raise webob.exc.HTTPBadRequest(explanation=msg)
# force is True means share extend will extend directly, is False
# means will go through scheduler. Default value is False,
try:
force = strutils.bool_from_string(body.get(
action, body.get('extend'))['force'], strict=True)
except KeyError:
force = False
except (ValueError, TypeError):
msg = (_('Invalid boolean force : %(value)s') %
{'value': body.get('extend')['force']})
raise webob.exc.HTTPBadRequest(explanation=msg)
return share, size, force
def _get_valid_shrink_parameters(self, context, id, body, action):
try:
share = self.share_api.get(context, id)
except exception.NotFound as e:
raise webob.exc.HTTPNotFound(explanation=six.text_type(e))
try:
size = int(body.get(action, body.get('shrink'))['new_size'])
except (KeyError, ValueError, TypeError):
msg = _("New share size must be specified as an integer.")
raise webob.exc.HTTPBadRequest(explanation=msg)

View File

@ -391,11 +391,19 @@ class ShareController(shares.ShareMixin,
@wsgi.action('os-extend')
def extend_legacy(self, req, id, body):
"""Extend size of a share."""
body.get('os-extend', {}).pop('force', None)
return self._extend(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.Controller.api_version('2.7', '2.63')
@wsgi.action('extend')
def extend(self, req, id, body):
"""Extend size of a share."""
body.get('extend', {}).pop('force', None)
return self._extend(req, id, body)
@wsgi.Controller.api_version('2.64') # noqa
@wsgi.action('extend')
def extend(self, req, id, body): # pylint: disable=function-redefined # noqa F811
"""Extend size of a share."""
return self._extend(req, id, body)

View File

@ -422,6 +422,17 @@ shares_policies = [
],
deprecated_rule=deprecated_share_extend
),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'force_extend',
check_str=base.SYSTEM_ADMIN_OR_PROJECT_ADMIN,
scope_types=['system', 'project'],
description="Force extend share.",
operations=[
{
'method': 'POST',
'path': '/shares/{share_id}/action',
}
]),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'shrink',
check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER,

View File

@ -65,7 +65,7 @@ MAPPING = {
class SchedulerManager(manager.Manager):
"""Chooses a host to create shares."""
RPC_API_VERSION = '1.10'
RPC_API_VERSION = '1.11'
def __init__(self, scheduler_driver=None, service_name=None,
*args, **kwargs):
@ -316,3 +316,35 @@ class SchedulerManager(manager.Manager):
@coordination.synchronized('locked-clean-expired-messages')
def _clean_expired_messages(self, context):
self.message_api.cleanup_expired_messages(context)
def extend_share(self, context, share_id, new_size, reservations,
request_spec=None, filter_properties=None):
def _extend_share_set_error(self, context, ex, request_spec):
share_state = {'status': constants.STATUS_AVAILABLE}
self._set_share_state_and_notify('extend_share', share_state,
context, ex, request_spec)
share = db.share_get(context, share_id)
try:
target_host = self.driver.host_passes_filters(
context,
share['host'],
request_spec, filter_properties)
target_host.consume_from_share(
{'size': int(new_size) - share['size']})
share_rpcapi.ShareAPI().extend_share(context, share, new_size,
reservations)
except exception.NoValidHost as ex:
quota.QUOTAS.rollback(context, reservations,
project_id=share['project_id'],
user_id=share['user_id'],
share_type_id=share['share_type_id'])
_extend_share_set_error(self, context, ex, request_spec)
self.message_api.create(
context,
message_field.Action.EXTEND,
share['project_id'],
resource_type=message_field.Resource.SHARE,
resource_id=share['id'],
exception=ex)

View File

@ -43,9 +43,10 @@ class SchedulerAPI(object):
1.8 - Rename create_consistency_group -> create_share_group method
1.9 - Add cached parameter to get_pools method
1.10 - Add timestamp to update_service_capabilities
1.11 - Add extend_share
"""
RPC_API_VERSION = '1.10'
RPC_API_VERSION = '1.11'
def __init__(self):
super(SchedulerAPI, self).__init__()
@ -144,3 +145,17 @@ class SchedulerAPI(object):
driver_options=driver_options,
request_spec=request_spec,
filter_properties=filter_properties)
def extend_share(self, context, share_id, new_size, reservations,
request_spec, filter_properties=None):
call_context = self.client.prepare(version='1.11')
msg_args = {
'share_id': share_id,
'new_size': new_size,
'reservations': reservations,
'request_spec': request_spec,
'filter_properties': filter_properties,
}
return call_context.cast(context, 'extend_share', **msg_args)

View File

@ -2127,8 +2127,11 @@ class API(base.Base):
def get_share_network(self, context, share_net_id):
return self.db.share_network_get(context, share_net_id)
def extend(self, context, share, new_size):
policy.check_policy(context, 'share', 'extend')
def extend(self, context, share, new_size, force=False):
if force:
policy.check_policy(context, 'share', 'force_extend')
else:
policy.check_policy(context, 'share', 'extend')
if share['status'] != constants.STATUS_AVAILABLE:
msg_params = {
@ -2222,7 +2225,15 @@ class API(base.Base):
message=msg)
self.update(context, share, {'status': constants.STATUS_EXTENDING})
self.share_rpcapi.extend_share(context, share, new_size, reservations)
if force:
self.share_rpcapi.extend_share(context, share,
new_size, reservations)
else:
share_type = share_types.get_share_type(
context, share['instance']['share_type_id'])
request_spec = self._get_request_spec_dict(share, share_type)
self.scheduler_rpcapi.extend_share(context, share['id'], new_size,
reservations, request_spec)
LOG.info("Extend share request issued successfully.",
resource=share)

View File

@ -1112,7 +1112,7 @@ class ShareActionsTest(test.TestCase):
share_api.API.get.assert_called_once_with(mock.ANY, id)
share_api.API.extend.assert_called_once_with(
mock.ANY, share, int(size))
mock.ANY, share, int(size), force=False)
self.assertEqual(202, actual_response.status_int)
@ddt.data({"os-extend": ""},

View File

@ -2418,7 +2418,7 @@ class ShareActionsTest(test.TestCase):
share_api.API.get.assert_called_once_with(mock.ANY, id)
share_api.API.extend.assert_called_once_with(
mock.ANY, share, int(size))
mock.ANY, share, int(size), force=False)
self.assertEqual(202, actual_response.status_int)
@ddt.data({"os-extend": ""},

View File

@ -130,3 +130,13 @@ class SchedulerRpcAPITestCase(test.TestCase):
request_spec='fake_request_spec',
filter_properties='filter_properties',
version='1.6')
def test_extend_share(self):
self._test_scheduler_api('extend_share',
rpc_method='cast',
share_id='share_id',
new_size='fake_size',
reservations='fake_reservations',
request_spec='fake_request_spec',
filter_properties='filter_properties',
version='1.11',)

View File

@ -2948,9 +2948,19 @@ class ShareAPITestCase(test.TestCase):
project_id='fake',
is_admin=False
)
fake_type = {
'id': 'fake_type_id',
'extra_specs': {
'snapshot_support': False,
'create_share_from_snapshot_support': False,
'driver_handles_share_servers': False,
},
}
new_size = 123
size_increase = int(new_size) - share['size']
self.mock_object(quota.QUOTAS, 'reserve')
self.mock_object(share_types, 'get_share_type',
mock.Mock(return_value=fake_type))
self.api.extend(diff_user_context, share, new_size)
@ -2982,15 +2992,19 @@ class ShareAPITestCase(test.TestCase):
new_replica_size = size_increase * replica_amount
expected_deltas.update({'replica_gigabytes': new_replica_size})
self.mock_object(self.api, 'update')
self.mock_object(self.api.share_rpcapi, 'extend_share')
self.mock_object(self.api.scheduler_rpcapi, 'extend_share')
self.mock_object(quota.QUOTAS, 'reserve')
self.mock_object(share_types, 'get_share_type')
self.mock_object(share_types, 'provision_filter_on_size')
self.mock_object(self.api, '_get_request_spec_dict')
self.api.extend(self.context, share, new_size)
self.api.update.assert_called_once_with(
self.context, share, {'status': constants.STATUS_EXTENDING})
self.api.share_rpcapi.extend_share.assert_called_once_with(
self.context, share, new_size, mock.ANY
self.api.scheduler_rpcapi.extend_share.assert_called_once_with(
self.context, share['id'], new_size, mock.ANY, mock.ANY
)
quota.QUOTAS.reserve.assert_called_once_with(
self.context, **expected_deltas)

View File

@ -0,0 +1,7 @@
---
fixes:
- |
`Launchpad bug 1855391 <https://bugs.launchpad.net/manila/+bug/1855391>`_
has been fixed. The action of extend share will go through scheduler, if
there is no available share backend host, the share will rollback to
available state and create an user message about extend.