Merge "Make API calls in Volumes view parallel"

This commit is contained in:
Zuul
2018-11-15 14:28:34 +00:00
committed by Gerrit Code Review
3 changed files with 95 additions and 26 deletions

View File

@@ -15,7 +15,6 @@
""" """
Admin views for managing volumes and snapshots. Admin views for managing volumes and snapshots.
""" """
from collections import OrderedDict
from django.conf import settings from django.conf import settings
from django.urls import reverse from django.urls import reverse
@@ -37,6 +36,7 @@ from openstack_dashboard.dashboards.admin.volumes \
import tabs as volumes_tabs import tabs as volumes_tabs
from openstack_dashboard.dashboards.project.volumes \ from openstack_dashboard.dashboards.project.volumes \
import views as volumes_views import views as volumes_views
from openstack_dashboard.utils import futurist_utils
class VolumesView(tables.PagedTableMixin, volumes_views.VolumeTableMixIn, class VolumesView(tables.PagedTableMixin, volumes_views.VolumeTableMixIn,
@@ -61,10 +61,55 @@ class VolumesView(tables.PagedTableMixin, volumes_views.VolumeTableMixIn,
self.table.needs_filter_first = True self.table.needs_filter_first = True
return volumes 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: if 'project' in filters:
# Keystone returns a tuple ([],false) where the first element is futurist_utils.call_functions_parallel(
# tenant list that's why the 0 is hardcoded below _task_get_tenants,
tenants = keystone.tenant_list(self.request)[0] _task_get_instances,
_task_get_volumes_snapshots
)
tenant_ids = [t.id for t in tenants tenant_ids = [t.id for t in tenants
if t.name == filters['project']] if t.name == filters['project']]
if not tenant_ids: if not tenant_ids:
@@ -73,26 +118,18 @@ class VolumesView(tables.PagedTableMixin, volumes_views.VolumeTableMixIn,
for id in tenant_ids: for id in tenant_ids:
filters['project_id'] = id filters['project_id'] = id
volumes += self._get_volumes(search_opts=filters) volumes += self._get_volumes(search_opts=filters)
attached_instance_ids = self._get_attached_instance_ids(volumes)
else: 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( self._set_volume_attributes(
volumes, instances, volume_ids_with_snapshots) 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: for volume in volumes:
tenant_id = getattr(volume, "os-vol-tenant-attr:tenant_id", None) tenant_id = getattr(volume, "os-vol-tenant-attr:tenant_id", None)
tenant = tenant_dict.get(tenant_id, None) tenant = tenant_dict.get(tenant_id, None)

View File

@@ -1968,6 +1968,7 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
self.assertEqual(7, self.mock_tenant_absolute_limits.call_count) self.assertEqual(7, self.mock_tenant_absolute_limits.call_count)
@test.create_mocks({ @test.create_mocks({
api.nova: ['server_list'],
cinder: ['volume_list_paged', cinder: ['volume_list_paged',
'volume_snapshot_list', 'volume_snapshot_list',
'tenant_absolute_limits', 'tenant_absolute_limits',
@@ -1987,6 +1988,10 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
transfer.id, transfer.id,
transfer.auth_key) transfer.auth_key)
self.assertEqual(2, self.mock_tenant_absolute_limits.call_count) 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') @mock.patch.object(cinder, 'transfer_get')
def test_download_transfer_credentials(self, mock_transfer): def test_download_transfer_credentials(self, mock_transfer):

View File

@@ -41,6 +41,7 @@ from openstack_dashboard.api import nova
from openstack_dashboard import exceptions as dashboard_exception from openstack_dashboard import exceptions as dashboard_exception
from openstack_dashboard.usage import quotas from openstack_dashboard.usage import quotas
from openstack_dashboard.utils import filters from openstack_dashboard.utils import filters
from openstack_dashboard.utils import futurist_utils
from openstack_dashboard.dashboards.project.volumes \ from openstack_dashboard.dashboards.project.volumes \
import forms as volume_forms import forms as volume_forms
@@ -71,9 +72,7 @@ class VolumeTableMixIn(object):
_('Unable to retrieve volume list.')) _('Unable to retrieve volume list.'))
return [] return []
def _get_instances(self, search_opts=None, instance_ids=None): def _get_instances(self, search_opts=None):
if not instance_ids:
return []
try: try:
# TODO(tsufiev): we should pass attached_instance_ids to # TODO(tsufiev): we should pass attached_instance_ids to
# nova.server_list as soon as Nova API allows for this # nova.server_list as soon as Nova API allows for this
@@ -149,10 +148,38 @@ class VolumesView(tables.PagedTableMixin, VolumeTableMixIn,
page_title = _("Volumes") page_title = _("Volumes")
def get_data(self): def get_data(self):
volumes = self._get_volumes() volumes = []
attached_instance_ids = self._get_attached_instance_ids(volumes) attached_instance_ids = []
instances = self._get_instances(instance_ids=attached_instance_ids) instances = []
volume_ids_with_snapshots = self._get_volumes_ids_with_snapshots() 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( self._set_volume_attributes(
volumes, instances, volume_ids_with_snapshots) volumes, instances, volume_ids_with_snapshots)
self._get_groups(volumes) self._get_groups(volumes)