Fix task_state field shown on API < 2.5

After Share Migration was included, the task_state field was
added to be displayed on GET requests, but API versions prior to
2.5 should not see this field. This patch fixes that by using
microversions.

Closes-bug: #1494746
Change-Id: Ie755ae53fe8efdf1702a0ecbabf022a5fe4beb93
This commit is contained in:
Rodrigo Barbieri 2015-09-14 14:51:44 -03:00
parent 1b9a801d1e
commit 350dfb8b4a
3 changed files with 43 additions and 57 deletions

View File

@ -22,6 +22,7 @@ class ViewBuilder(common.ViewBuilder):
_collection_name = 'shares' _collection_name = 'shares'
_detail_version_modifiers = [ _detail_version_modifiers = [
"add_consistency_group_fields", "add_consistency_group_fields",
"add_task_state_field",
"modify_share_type_field", "modify_share_type_field",
] ]
@ -65,7 +66,6 @@ class ViewBuilder(common.ViewBuilder):
'availability_zone': share.get('availability_zone'), 'availability_zone': share.get('availability_zone'),
'created_at': share.get('created_at'), 'created_at': share.get('created_at'),
'status': share.get('status'), 'status': share.get('status'),
'task_state': share.get('task_state'),
'name': share.get('display_name'), 'name': share.get('display_name'),
'description': share.get('display_description'), 'description': share.get('display_description'),
'project_id': share.get('project_id'), 'project_id': share.get('project_id'),
@ -95,6 +95,10 @@ class ViewBuilder(common.ViewBuilder):
share_dict['source_cgsnapshot_member_id'] = share.get( share_dict['source_cgsnapshot_member_id'] = share.get(
'source_cgsnapshot_member_id') 'source_cgsnapshot_member_id')
@common.ViewBuilder.versioned_method("2.5", None, True)
def add_task_state_field(self, share_dict, share):
share_dict['task_state'] = share.get('task_state')
@common.ViewBuilder.versioned_method("2.6") @common.ViewBuilder.versioned_method("2.6")
def modify_share_type_field(self, share_dict, share): def modify_share_type_field(self, share_dict, share):
share_type = share.get('share_type_id') share_type = share.get('share_type_id')

View File

@ -101,7 +101,6 @@ class ShareApiTest(test.TestCase):
'snapshot_id': '2', 'snapshot_id': '2',
'share_network_id': None, 'share_network_id': None,
'status': 'fakestatus', 'status': 'fakestatus',
'task_state': None,
'share_type': '1', 'share_type': '1',
'volume_type': '1', 'volume_type': '1',
'is_public': False, 'is_public': False,
@ -691,17 +690,11 @@ class ShareApiTest(test.TestCase):
def test_share_list_detail_with_search_opts_by_admin(self): def test_share_list_detail_with_search_opts_by_admin(self):
self._share_list_detail_with_search_opts(use_admin_context=True) self._share_list_detail_with_search_opts(use_admin_context=True)
def test_share_list_detail(self): def _list_detail_common_expected(self):
self.mock_object(share_api.API, 'get_all', return {
stubs.stub_share_get_all_by_project)
env = {'QUERY_STRING': 'name=Share+Test+Name'}
req = fakes.HTTPRequest.blank('/shares/detail', environ=env)
res_dict = self.controller.detail(req)
expected = {
'shares': [ 'shares': [
{ {
'status': 'fakestatus', 'status': 'fakestatus',
'task_state': None,
'description': 'displaydesc', 'description': 'displaydesc',
'export_location': 'fake_location', 'export_location': 'fake_location',
'export_locations': ['fake_location', 'fake_location2'], 'export_locations': ['fake_location', 'fake_location2'],
@ -732,57 +725,39 @@ class ShareApiTest(test.TestCase):
} }
] ]
} }
def _list_detail_test_common(self, req, expected):
self.mock_object(share_api.API, 'get_all',
stubs.stub_share_get_all_by_project)
res_dict = self.controller.detail(req)
self.assertEqual(expected, res_dict) self.assertEqual(expected, res_dict)
self.assertEqual(res_dict['shares'][0]['volume_type'], self.assertEqual(res_dict['shares'][0]['volume_type'],
res_dict['shares'][0]['share_type']) res_dict['shares'][0]['share_type'])
def test_share_list_detail(self):
env = {'QUERY_STRING': 'name=Share+Test+Name'}
req = fakes.HTTPRequest.blank('/shares/detail', environ=env)
expected = self._list_detail_common_expected()
self._list_detail_test_common(req, expected)
def test_share_list_detail_with_consistency_group(self): def test_share_list_detail_with_consistency_group(self):
self.mock_object(share_api.API, 'get_all',
stubs.stub_share_get_all_by_project)
env = {'QUERY_STRING': 'name=Share+Test+Name'} env = {'QUERY_STRING': 'name=Share+Test+Name'}
req = fakes.HTTPRequest.blank('/shares/detail', environ=env, req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
version="2.4") version="2.4")
res_dict = self.controller.detail(req) expected = self._list_detail_common_expected()
expected = { expected['shares'][0]['consistency_group_id'] = None
'shares': [ expected['shares'][0]['source_cgsnapshot_member_id'] = None
{ self._list_detail_test_common(req, expected)
'status': 'fakestatus',
'task_state': None, def test_share_list_detail_with_task_state(self):
'description': 'displaydesc', env = {'QUERY_STRING': 'name=Share+Test+Name'}
'export_location': 'fake_location', req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
'export_locations': ['fake_location', 'fake_location2'], version="2.5", experimental=True)
'availability_zone': 'fakeaz', expected = self._list_detail_common_expected()
'name': 'displayname', expected['shares'][0]['consistency_group_id'] = None
'share_proto': 'FAKEPROTO', expected['shares'][0]['source_cgsnapshot_member_id'] = None
'metadata': {}, expected['shares'][0]['task_state'] = None
'project_id': 'fakeproject', self._list_detail_test_common(req, expected)
'host': 'fakehost',
'id': '1',
'snapshot_id': '2',
'share_network_id': None,
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
'size': 1,
'share_type': '1',
'volume_type': '1',
'is_public': False,
'consistency_group_id': None,
'source_cgsnapshot_member_id': None,
'links': [
{
'href': 'http://localhost/v1/fake/shares/1',
'rel': 'self'
},
{
'href': 'http://localhost/fake/shares/1',
'rel': 'bookmark'
}
],
}
]
}
self.assertEqual(expected, res_dict)
self.assertEqual(res_dict['shares'][0]['volume_type'],
res_dict['shares'][0]['share_type'])
def test_remove_invalid_options(self): def test_remove_invalid_options(self):
ctx = context.RequestContext('fakeuser', 'fakeproject', is_admin=False) ctx = context.RequestContext('fakeuser', 'fakeproject', is_admin=False)

View File

@ -220,8 +220,15 @@ class SharesV2Client(shares_client.SharesClient):
"""Get detailed list of shares w/o filters.""" """Get detailed list of shares w/o filters."""
return self.list_shares(detailed=True, params=params, version=version) return self.list_shares(detailed=True, params=params, version=version)
def get_share(self, share_id, version=LATEST_MICROVERSION): def get_share(self, share_id, version=LATEST_MICROVERSION,
resp, body = self.get("shares/%s" % share_id, version=version) experimental=False):
headers = None
extra_headers = False
if experimental:
headers = EXPERIMENTAL
extra_headers = True
resp, body = self.get("shares/%s" % share_id, version=version,
headers=headers, extra_headers=extra_headers)
self.expected_success(200, resp.status) self.expected_success(200, resp.status)
return self._parse_resp(body) return self._parse_resp(body)
@ -493,12 +500,12 @@ class SharesV2Client(shares_client.SharesClient):
def wait_for_migration_completed(self, share_id, dest_host): def wait_for_migration_completed(self, share_id, dest_host):
"""Waits for a share to migrate to a certain host.""" """Waits for a share to migrate to a certain host."""
share = self.get_share(share_id) share = self.get_share(share_id, "2.5", True)
migration_timeout = CONF.share.migration_timeout migration_timeout = CONF.share.migration_timeout
start = int(time.time()) start = int(time.time())
while share['task_state'] != 'migration_success': while share['task_state'] != 'migration_success':
time.sleep(self.build_interval) time.sleep(self.build_interval)
share = self.get_share(share_id) share = self.get_share(share_id, "2.5", True)
if share['task_state'] == 'migration_success': if share['task_state'] == 'migration_success':
return share return share
elif share['task_state'] == 'migration_error': elif share['task_state'] == 'migration_error':