WIP:Optimize the query logic for share network list

1: As admin user, query share network list with specified
   "security_service_id" and "project_id" search opts, will
   get wrong result.
2: put "created_since", "created_before" search opts into database to
   Increase query speed

Closes-Bug: #1923008
Change-Id: I49e412cb6c98fcda67531ff915b3b4c3edc64476
This commit is contained in:
haixin 2021-04-09 16:23:24 +08:00
parent ad4315eb36
commit 596a435dd0
4 changed files with 118 additions and 81 deletions

View File

@ -162,41 +162,39 @@ class ShareNetworkController(wsgi.Controller, wsgi.AdminActionsMixin):
context = req.environ['manila.context']
search_opts = {}
search_opts.update(req.GET)
filters = {}
if 'security_service_id' in search_opts:
networks = db_api.share_network_get_all_by_security_service(
context, search_opts['security_service_id'])
elif context.is_admin and 'project_id' in search_opts:
networks = db_api.share_network_get_all_by_project(
context, search_opts['project_id'])
elif context.is_admin and utils.is_all_tenants(search_opts):
networks = db_api.share_network_get_all(context)
else:
networks = db_api.share_network_get_all_by_project(
context,
context.project_id)
# if not context.is_admin, will ignore project_id and all_tenants here,
# in database will auto add context.project_id to search_opts.
if context.is_admin:
if 'project_id' in search_opts:
# if specified project_id, will not use all_tenants
filters['project_id'] = search_opts['project_id']
elif not utils.is_all_tenants(search_opts):
# if not specified project_id and all_tenants, will get
# share networks in admin project.
filters['project_id'] = context.project_id
date_parsing_error_msg = '''%s is not in yyyy-mm-dd format.'''
if 'created_since' in search_opts:
try:
created_since = timeutils.parse_strtime(
search_opts['created_since'],
fmt="%Y-%m-%d")
except ValueError:
msg = date_parsing_error_msg % search_opts['created_since']
raise exc.HTTPBadRequest(explanation=msg)
networks = [network for network in networks
if network['created_at'] >= created_since]
if 'created_before' in search_opts:
try:
created_before = timeutils.parse_strtime(
search_opts['created_before'],
fmt="%Y-%m-%d")
except ValueError:
msg = date_parsing_error_msg % search_opts['created_before']
raise exc.HTTPBadRequest(explanation=msg)
networks = [network for network in networks
if network['created_at'] <= created_before]
for time_comparison_filter in ['created_since', 'created_before']:
if time_comparison_filter in search_opts:
time_str = search_opts.get(time_comparison_filter)
try:
parsed_time = timeutils.parse_strtime(time_str,
fmt="%Y-%m-%d")
except ValueError:
msg = date_parsing_error_msg % time_str
raise exc.HTTPBadRequest(explanation=msg)
filters[time_comparison_filter] = parsed_time
if 'security_service_id' in search_opts:
filters['security_service_id'] = search_opts.get(
'security_service_id')
networks = db_api.share_network_get_all_by_filter(context,
filters=filters)
opts_to_remove = [
'all_tenants',
'created_since',

View File

@ -868,6 +868,11 @@ def share_network_get(context, id):
return IMPL.share_network_get(context, id)
def share_network_get_all_by_filter(context, filters=None):
"""Get all share network DB records for the given filter."""
return IMPL.share_network_get_all_by_filter(context, filters=filters)
def share_network_get_all(context):
"""Get all share network DB records."""
return IMPL.share_network_get_all(context)

View File

@ -3847,6 +3847,33 @@ def share_network_get(context, id, session=None):
return result
@require_context
def share_network_get_all_by_filter(context, filters=None):
model_sn = models.ShareNetwork
session = get_session()
with session.begin():
query = _network_get_query(context,
session=session)
legal_filter_keys = ('project_id', 'created_since', 'created_before')
if not filters:
filters = {}
query = exact_filter(query, model_sn, filters, legal_filter_keys)
if 'security_service_id' in filters:
security_service_id = filters.get('security_service_id')
query = query.join(
models.ShareNetworkSecurityServiceAssociation,
models.ShareNetwork.id == models.
ShareNetworkSecurityServiceAssociation.
share_network_id).filter_by(
security_service_id=security_service_id, deleted=0)
return query.all()
@require_context
def share_network_get_all(context):
return _network_get_query(context).all()

View File

@ -537,14 +537,13 @@ class ShareNetworkAPITest(test.TestCase):
def test_index_no_filters(self):
networks = [fake_share_network]
with mock.patch.object(db_api,
'share_network_get_all_by_project',
'share_network_get_all_by_filter',
mock.Mock(return_value=networks)):
result = self.controller.index(self.req)
db_api.share_network_get_all_by_project.assert_called_once_with(
self.context,
self.context.project_id)
db_api.share_network_get_all_by_filter.assert_called_once_with(
self.context, filters={})
self.assertEqual(1, len(result[share_networks.RESOURCES_NAME]))
self._check_share_network_view_shortened(
@ -554,55 +553,56 @@ class ShareNetworkAPITest(test.TestCase):
def test_index_detailed(self):
networks = [fake_share_network]
with mock.patch.object(db_api,
'share_network_get_all_by_project',
'share_network_get_all_by_filter',
mock.Mock(return_value=networks)):
result = self.controller.detail(self.req)
db_api.share_network_get_all_by_project.assert_called_once_with(
self.context,
self.context.project_id)
db_api.share_network_get_all_by_filter.assert_called_once_with(
self.context, filters={})
self.assertEqual(1, len(result[share_networks.RESOURCES_NAME]))
self._check_share_network_view(
result[share_networks.RESOURCES_NAME][0],
fake_share_network)
@mock.patch.object(db_api, 'share_network_get_all_by_security_service',
@mock.patch.object(db_api, 'share_network_get_all_by_filter',
mock.Mock())
def test_index_filter_by_security_service(self):
db_api.share_network_get_all_by_security_service.return_value = [
db_api.share_network_get_all_by_filter.return_value = [
fake_share_network_with_ss]
req = fakes.HTTPRequest.blank(
'/share_networks?security_service_id=fake-ss-id')
result = self.controller.index(req)
(db_api.share_network_get_all_by_security_service.
filters = {'security_service_id': 'fake-ss-id'}
(db_api.share_network_get_all_by_filter.
assert_called_once_with(req.environ['manila.context'],
'fake-ss-id'))
filters=filters))
self.assertEqual(1, len(result[share_networks.RESOURCES_NAME]))
self._check_share_network_view_shortened(
result[share_networks.RESOURCES_NAME][0],
fake_sn_with_ss_shortened)
@mock.patch.object(db_api, 'share_network_get_all_by_project', mock.Mock())
@mock.patch.object(db_api, 'share_network_get_all_by_filter', mock.Mock())
def test_index_all_tenants_non_admin_context(self):
req = fakes.HTTPRequest.blank(
'/share_networks?all_tenants=1')
fake_context = req.environ['manila.context']
db_api.share_network_get_all_by_project.return_value = []
db_api.share_network_get_all_by_filter.return_value = []
self.controller.index(req)
db_api.share_network_get_all_by_project.assert_called_with(
fake_context, fake_context.project_id)
db_api.share_network_get_all_by_filter.assert_called_with(
fake_context, filters={})
@mock.patch.object(db_api, 'share_network_get_all', mock.Mock())
@mock.patch.object(db_api, 'share_network_get_all_by_filter', mock.Mock())
def test_index_all_tenants_admin_context(self):
db_api.share_network_get_all.return_value = [fake_share_network]
db_api.share_network_get_all_by_filter.return_value = [
fake_share_network]
req = fakes.HTTPRequest.blank(
'/share_networks?all_tenants=1',
use_admin_context=True)
result = self.controller.index(req)
db_api.share_network_get_all.assert_called_once_with(
req.environ['manila.context'])
db_api.share_network_get_all_by_filter.assert_called_once_with(
req.environ['manila.context'], filters={})
self.assertEqual(1, len(result[share_networks.RESOURCES_NAME]))
self._check_share_network_view_shortened(
result[share_networks.RESOURCES_NAME][0],
@ -616,10 +616,9 @@ class ShareNetworkAPITest(test.TestCase):
self.assertRaises(exception.InvalidInput, self.controller.index, req)
@mock.patch.object(db_api, 'share_network_get_all_by_project', mock.Mock())
@mock.patch.object(db_api, 'share_network_get_all', mock.Mock())
@mock.patch.object(db_api, 'share_network_get_all_by_filter', mock.Mock())
def test_index_all_tenants_with_value_zero(self):
db_api.share_network_get_all_by_project.return_value = [
db_api.share_network_get_all_by_filter.return_value = [
fake_share_network]
req = fakes.HTTPRequest.blank(
'/share_networks?all_tenants=0',
@ -631,49 +630,50 @@ class ShareNetworkAPITest(test.TestCase):
self._check_share_network_view_shortened(
result[share_networks.RESOURCES_NAME][0],
fake_share_network_shortened)
db_api.share_network_get_all_by_project.assert_called_once_with(
req.environ['manila.context'], self.context.project_id)
db_api.share_network_get_all.assert_not_called()
db_api.share_network_get_all_by_filter.assert_called_once_with(
req.environ['manila.context'], filters={})
@mock.patch.object(db_api, 'share_network_get_all_by_project', mock.Mock())
@mock.patch.object(db_api, 'share_network_get_all_by_filter', mock.Mock())
def test_index_filter_by_project_id_non_admin_context(self):
req = fakes.HTTPRequest.blank(
'/share_networks?project_id=fake project')
fake_context = req.environ['manila.context']
db_api.share_network_get_all_by_project.return_value = []
db_api.share_network_get_all_by_filter.return_value = []
self.controller.index(req)
db_api.share_network_get_all_by_project.assert_called_with(
fake_context, fake_context.project_id)
db_api.share_network_get_all_by_filter.assert_called_with(
fake_context, filters={})
@mock.patch.object(db_api, 'share_network_get_all_by_project', mock.Mock())
@mock.patch.object(db_api, 'share_network_get_all_by_filter', mock.Mock())
def test_index_filter_by_project_id_admin_context(self):
db_api.share_network_get_all_by_project.return_value = [
db_api.share_network_get_all_by_filter.return_value = [
fake_share_network_with_ss
]
req = fakes.HTTPRequest.blank(
'/share_networks?project_id=fake',
use_admin_context=True)
result = self.controller.index(req)
db_api.share_network_get_all_by_project.assert_called_once_with(
req.environ['manila.context'], 'fake')
filters = {'project_id': 'fake'}
db_api.share_network_get_all_by_filter.assert_called_once_with(
req.environ['manila.context'], filters=filters)
self.assertEqual(1, len(result[share_networks.RESOURCES_NAME]))
self._check_share_network_view_shortened(
result[share_networks.RESOURCES_NAME][0],
fake_sn_with_ss_shortened)
@mock.patch.object(db_api, 'share_network_get_all_by_security_service',
@mock.patch.object(db_api, 'share_network_get_all_by_filter',
mock.Mock())
def test_index_filter_by_ss_and_project_id_admin_context(self):
db_api.share_network_get_all_by_security_service.return_value = [
db_api.share_network_get_all_by_filter.return_value = [
fake_share_network_with_ss
]
req = fakes.HTTPRequest.blank(
'/share_networks?security_service_id=fake-ss-id&project_id=fake',
use_admin_context=True)
result = self.controller.index(req)
(db_api.share_network_get_all_by_security_service.
assert_called_once_with(req.environ['manila.context'],
'fake-ss-id'))
filters = {'project_id': 'fake',
'security_service_id': 'fake-ss-id'}
db_api.share_network_get_all_by_filter.assert_called_once_with(
req.environ['manila.context'], filters=filters)
self.assertEqual(1, len(result[share_networks.RESOURCES_NAME]))
self._check_share_network_view_shortened(
result[share_networks.RESOURCES_NAME][0],
@ -686,20 +686,21 @@ class ShareNetworkAPITest(test.TestCase):
('name=foo&description~=ds', 1),
('name~=foo&description=ds', 1))
@ddt.unpack
@mock.patch.object(db_api, 'share_network_get_all_by_project',
@mock.patch.object(db_api, 'share_network_get_all_by_filter',
mock.Mock())
def test_index_filter_by_name_and_description(
self, filter, share_network_number):
fake_objs = [{'name': 'fo2', 'description': 'd2', 'id': 'fake1'},
{'name': 'foo', 'description': 'ds', 'id': 'fake2'},
{'name': 'foo1', 'description': 'ds1', 'id': 'fake3'}]
db_api.share_network_get_all_by_project.return_value = fake_objs
db_api.share_network_get_all_by_filter.return_value = fake_objs
req = fakes.HTTPRequest.blank(
'/share_networks?' + filter,
use_admin_context=True, version='2.36')
result = self.controller.index(req)
db_api.share_network_get_all_by_project.assert_called_with(
req.environ['manila.context'], self.context.project_id)
db_api.share_network_get_all_by_filter.assert_called_with(
req.environ['manila.context'],
{'project_id': self.context.project_id})
self.assertEqual(share_network_number,
len(result[share_networks.RESOURCES_NAME]))
if share_network_number > 0:
@ -709,7 +710,7 @@ class ShareNetworkAPITest(test.TestCase):
self._check_share_network_view_shortened(
result[share_networks.RESOURCES_NAME][1], fake_objs[2])
@mock.patch.object(db_api, 'share_network_get_all_by_project',
@mock.patch.object(db_api, 'share_network_get_all_by_filter',
mock.Mock())
def test_index_all_filter_opts(self):
valid_filter_opts = {
@ -717,8 +718,7 @@ class ShareNetworkAPITest(test.TestCase):
'created_since': '1999-01-01',
'name': 'test-sn'
}
db_api.share_network_get_all_by_project.return_value = [
fake_share_network,
db_api.share_network_get_all_by_filter.return_value = [
fake_share_network_with_ss]
query_string = '/share-networks?' + parse.urlencode(sorted(
@ -727,9 +727,16 @@ class ShareNetworkAPITest(test.TestCase):
req = fakes.HTTPRequest.blank(query_string,
use_admin_context=use_admin_context)
result = self.controller.index(req)
db_api.share_network_get_all_by_project.assert_called_with(
req.environ['manila.context'],
'fake')
parsed_created_before = timeutils.parse_strtime(
valid_filter_opts['created_before'], fmt="%Y-%m-%d")
parsed_created_since = timeutils.parse_strtime(
valid_filter_opts['created_since'], fmt="%Y-%m-%d")
filters = {'created_before': parsed_created_before,
'created_since': parsed_created_since}
if use_admin_context:
filters['project_id'] = 'fake'
db_api.share_network_get_all_by_filter.assert_called_with(
req.environ['manila.context'], filters)
self.assertEqual(1, len(result[share_networks.RESOURCES_NAME]))
self._check_share_network_view_shortened(
result[share_networks.RESOURCES_NAME][0],