Make API calls in Volumes view parallel
In order to increase rendering speed, make api.cinder.volume_list_paged, api.nova.server_list and api.cinder.volume_snapshot_list parallel. For admin panel also api.keystone.tenant_list is parallelized. Closes-bug: #1660025 Partial-implements: blueprint fetch-resources-in-parallel Change-Id: Ic789ff98a59ba792d77103e7778883562e7977d2
This commit is contained in:
parent
cebedd9a07
commit
9810711e75
|
@ -15,7 +15,6 @@
|
|||
"""
|
||||
Admin views for managing volumes and snapshots.
|
||||
"""
|
||||
from collections import OrderedDict
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
|
@ -37,6 +36,7 @@ from openstack_dashboard.dashboards.admin.volumes \
|
|||
import tabs as volumes_tabs
|
||||
from openstack_dashboard.dashboards.project.volumes \
|
||||
import views as volumes_views
|
||||
from openstack_dashboard.utils import futurist_utils
|
||||
|
||||
|
||||
class VolumesView(tables.PagedTableMixin, volumes_views.VolumeTableMixIn,
|
||||
|
@ -61,10 +61,55 @@ class VolumesView(tables.PagedTableMixin, volumes_views.VolumeTableMixIn,
|
|||
self.table.needs_filter_first = True
|
||||
return volumes
|
||||
|
||||
volumes = []
|
||||
attached_instance_ids = []
|
||||
tenants = []
|
||||
tenant_dict = {}
|
||||
instances = []
|
||||
volume_ids_with_snapshots = []
|
||||
|
||||
def _task_get_tenants():
|
||||
# Gather our tenants to correlate against IDs
|
||||
try:
|
||||
tmp_tenants, __ = keystone.tenant_list(self.request)
|
||||
tenants.extend(tmp_tenants)
|
||||
tenant_dict.update([(t.id, t) for t in tenants])
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve volume project information.')
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
def _task_get_instances():
|
||||
# As long as Nova API does not allow passing attached_instance_ids
|
||||
# to nova.server_list, this call can be forged to pass anything
|
||||
# != None
|
||||
instances.extend(self._get_instances(
|
||||
search_opts={'all_tenants': True}))
|
||||
|
||||
# In volumes tab we don't need to know about the assignment
|
||||
# instance-image, therefore fixing it to an empty value
|
||||
for instance in instances:
|
||||
if hasattr(instance, 'image'):
|
||||
if isinstance(instance.image, dict):
|
||||
instance.image['name'] = "-"
|
||||
|
||||
def _task_get_volumes_snapshots():
|
||||
volume_ids_with_snapshots.extend(
|
||||
self._get_volumes_ids_with_snapshots(
|
||||
search_opts={'all_tenants': True}
|
||||
))
|
||||
|
||||
def _task_get_volumes():
|
||||
volumes.extend(self._get_volumes(search_opts=filters))
|
||||
attached_instance_ids.extend(
|
||||
self._get_attached_instance_ids(volumes))
|
||||
|
||||
if 'project' in filters:
|
||||
# Keystone returns a tuple ([],false) where the first element is
|
||||
# tenant list that's why the 0 is hardcoded below
|
||||
tenants = keystone.tenant_list(self.request)[0]
|
||||
futurist_utils.call_functions_parallel(
|
||||
_task_get_tenants,
|
||||
_task_get_instances,
|
||||
_task_get_volumes_snapshots
|
||||
)
|
||||
|
||||
tenant_ids = [t.id for t in tenants
|
||||
if t.name == filters['project']]
|
||||
if not tenant_ids:
|
||||
|
@ -73,26 +118,18 @@ class VolumesView(tables.PagedTableMixin, volumes_views.VolumeTableMixIn,
|
|||
for id in tenant_ids:
|
||||
filters['project_id'] = id
|
||||
volumes += self._get_volumes(search_opts=filters)
|
||||
attached_instance_ids = self._get_attached_instance_ids(volumes)
|
||||
else:
|
||||
volumes = self._get_volumes(search_opts=filters)
|
||||
futurist_utils.call_functions_parallel(
|
||||
_task_get_volumes,
|
||||
_task_get_tenants,
|
||||
_task_get_instances,
|
||||
_task_get_volumes_snapshots
|
||||
)
|
||||
|
||||
attached_instance_ids = self._get_attached_instance_ids(volumes)
|
||||
instances = self._get_instances(search_opts={'all_tenants': True},
|
||||
instance_ids=attached_instance_ids)
|
||||
volume_ids_with_snapshots = self._get_volumes_ids_with_snapshots(
|
||||
search_opts={'all_tenants': True})
|
||||
self._set_volume_attributes(
|
||||
volumes, instances, volume_ids_with_snapshots)
|
||||
|
||||
# Gather our tenants to correlate against IDs
|
||||
try:
|
||||
tenants, has_more = keystone.tenant_list(self.request)
|
||||
except Exception:
|
||||
tenants = []
|
||||
msg = _('Unable to retrieve volume project information.')
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
tenant_dict = OrderedDict([(t.id, t) for t in tenants])
|
||||
for volume in volumes:
|
||||
tenant_id = getattr(volume, "os-vol-tenant-attr:tenant_id", None)
|
||||
tenant = tenant_dict.get(tenant_id, None)
|
||||
|
|
|
@ -1968,6 +1968,7 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
|
|||
self.assertEqual(7, self.mock_tenant_absolute_limits.call_count)
|
||||
|
||||
@test.create_mocks({
|
||||
api.nova: ['server_list'],
|
||||
cinder: ['volume_list_paged',
|
||||
'volume_snapshot_list',
|
||||
'tenant_absolute_limits',
|
||||
|
@ -1987,6 +1988,10 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
|
|||
transfer.id,
|
||||
transfer.auth_key)
|
||||
self.assertEqual(2, self.mock_tenant_absolute_limits.call_count)
|
||||
self.mock_server_list.assert_called_once()
|
||||
self.mock_volume_list_paged.assert_called_once()
|
||||
self.mock_volume_snapshot_list.assert_called_once()
|
||||
self.mock_transfer_accept.assert_called_once()
|
||||
|
||||
@mock.patch.object(cinder, 'transfer_get')
|
||||
def test_download_transfer_credentials(self, mock_transfer):
|
||||
|
|
|
@ -41,6 +41,7 @@ from openstack_dashboard.api import nova
|
|||
from openstack_dashboard import exceptions as dashboard_exception
|
||||
from openstack_dashboard.usage import quotas
|
||||
from openstack_dashboard.utils import filters
|
||||
from openstack_dashboard.utils import futurist_utils
|
||||
|
||||
from openstack_dashboard.dashboards.project.volumes \
|
||||
import forms as volume_forms
|
||||
|
@ -71,9 +72,7 @@ class VolumeTableMixIn(object):
|
|||
_('Unable to retrieve volume list.'))
|
||||
return []
|
||||
|
||||
def _get_instances(self, search_opts=None, instance_ids=None):
|
||||
if not instance_ids:
|
||||
return []
|
||||
def _get_instances(self, search_opts=None):
|
||||
try:
|
||||
# TODO(tsufiev): we should pass attached_instance_ids to
|
||||
# nova.server_list as soon as Nova API allows for this
|
||||
|
@ -149,10 +148,38 @@ class VolumesView(tables.PagedTableMixin, VolumeTableMixIn,
|
|||
page_title = _("Volumes")
|
||||
|
||||
def get_data(self):
|
||||
volumes = self._get_volumes()
|
||||
attached_instance_ids = self._get_attached_instance_ids(volumes)
|
||||
instances = self._get_instances(instance_ids=attached_instance_ids)
|
||||
volume_ids_with_snapshots = self._get_volumes_ids_with_snapshots()
|
||||
volumes = []
|
||||
attached_instance_ids = []
|
||||
instances = []
|
||||
volume_ids_with_snapshots = []
|
||||
|
||||
def _task_get_volumes():
|
||||
volumes.extend(self._get_volumes())
|
||||
attached_instance_ids.extend(
|
||||
self._get_attached_instance_ids(volumes))
|
||||
|
||||
def _task_get_instances():
|
||||
# As long as Nova API does not allow passing attached_instance_ids
|
||||
# to nova.server_list, this call can be forged to pass anything
|
||||
# != None
|
||||
instances.extend(self._get_instances())
|
||||
|
||||
# In volumes tab we don't need to know about the assignment
|
||||
# instance-image, therefore fixing it to an empty value
|
||||
for instance in instances:
|
||||
if hasattr(instance, 'image'):
|
||||
if isinstance(instance.image, dict):
|
||||
instance.image['name'] = "-"
|
||||
|
||||
def _task_get_volumes_snapshots():
|
||||
volume_ids_with_snapshots.extend(
|
||||
self._get_volumes_ids_with_snapshots())
|
||||
|
||||
futurist_utils.call_functions_parallel(
|
||||
_task_get_volumes,
|
||||
_task_get_instances,
|
||||
_task_get_volumes_snapshots)
|
||||
|
||||
self._set_volume_attributes(
|
||||
volumes, instances, volume_ids_with_snapshots)
|
||||
self._get_groups(volumes)
|
||||
|
|
Loading…
Reference in New Issue