diff --git a/doc/source/configuration/settings.rst b/doc/source/configuration/settings.rst index 6a2dac6e9f..b59a92d1bb 100644 --- a/doc/source/configuration/settings.rst +++ b/doc/source/configuration/settings.rst @@ -2575,6 +2575,19 @@ user friendly display name which will be rendered on the dashboard. If no display is specified for a storage policy, the storage policy name will be used verbatim. +SWIFT_PANEL_FULL_LISTING +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2026.1(Gazpacho) + +Perform full listing of containers and objects in the Swift panel. + +Default: ``True`` + +Note that the Swift panel does client side pagination and retrieves +all containers and objects from the API that can have an negative +effect on Horizon's resource consumption if this is True. + Django Settings =============== diff --git a/openstack_dashboard/api/swift.py b/openstack_dashboard/api/swift.py index a6a843449a..da36112741 100644 --- a/openstack_dashboard/api/swift.py +++ b/openstack_dashboard/api/swift.py @@ -164,12 +164,13 @@ def swift_object_exists(request, container_name, object_name): @safe_swift_exception def swift_get_containers(request, marker=None, prefix=None): limit = settings.API_RESULT_LIMIT + full_list = settings.SWIFT_PANEL_FULL_LISTING headers, containers = swift_api(request).get_account(limit=limit + 1, marker=marker, prefix=prefix, - full_listing=True) + full_listing=full_list) container_objs = [Container(c) for c in containers] - if len(container_objs) > limit: + if (len(container_objs) > limit): return (container_objs[0:-1], True) return (container_objs, False) @@ -261,16 +262,17 @@ def swift_delete_container(request, name): def swift_get_objects(request, container_name, prefix=None, marker=None, limit=None): limit = limit or settings.API_RESULT_LIMIT + full_listing = settings.SWIFT_PANEL_FULL_LISTING kwargs = dict(prefix=prefix, marker=marker, limit=limit + 1, delimiter=FOLDER_DELIMITER, - full_listing=True) + full_listing=full_listing) headers, objects = swift_api(request).get_container(container_name, **kwargs) object_objs = _objectify(objects, container_name) - if len(object_objs) > limit: + if (len(object_objs) > limit): return (object_objs[0:-1], True) return (object_objs, False) diff --git a/openstack_dashboard/dashboards/project/containers/templates/containers/ngindex.html b/openstack_dashboard/dashboards/project/containers/templates/containers/ngindex.html index de1eac7a26..43bdcb87b9 100644 --- a/openstack_dashboard/dashboards/project/containers/templates/containers/ngindex.html +++ b/openstack_dashboard/dashboards/project/containers/templates/containers/ngindex.html @@ -20,4 +20,8 @@ {% block main %} + + {% if SWIFT_PANEL_FULL_LISTING is False %} +
{% blocktrans %}Listing only {{ API_RESULT_LIMIT }} containers/objects because full listing is disabled in settings.{% endblocktrans %}
+ {% endif %} {% endblock %} diff --git a/openstack_dashboard/dashboards/project/containers/views.py b/openstack_dashboard/dashboards/project/containers/views.py index 2f0dbbfc0f..af6e8245a0 100644 --- a/openstack_dashboard/dashboards/project/containers/views.py +++ b/openstack_dashboard/dashboards/project/containers/views.py @@ -17,9 +17,16 @@ # under the License. +from django.conf import settings from django.views import generic class NgIndexView(generic.TemplateView): """View for managing Swift containers.""" template_name = 'project/containers/ngindex.html' + + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + context['API_RESULT_LIMIT'] = settings.API_RESULT_LIMIT + context['SWIFT_PANEL_FULL_LISTING'] = settings.SWIFT_PANEL_FULL_LISTING + return context diff --git a/openstack_dashboard/defaults.py b/openstack_dashboard/defaults.py index a44f0a36b4..ce98abd7a5 100644 --- a/openstack_dashboard/defaults.py +++ b/openstack_dashboard/defaults.py @@ -325,6 +325,14 @@ SWIFT_FILE_TRANSFER_CHUNK_SIZE = 512 * 1024 # name to be rendered. SWIFT_STORAGE_POLICY_DISPLAY_NAMES = {} +# Perform full listing of containers and objects in the Swift +# panel. Defaults to True. +# +# Note that the Swift panel does client side pagination and retrieves +# all containers and objects from the API that can have an negative +# effect on Horizon's resource consumption if this is True. +SWIFT_PANEL_FULL_LISTING = True + # NOTE: The default value of USER_MENU_LINKS will be set after loading # local_settings if it is not configured. USER_MENU_LINKS = None diff --git a/openstack_dashboard/test/unit/api/test_swift.py b/openstack_dashboard/test/unit/api/test_swift.py index 64d38f88d0..9e641190d4 100644 --- a/openstack_dashboard/test/unit/api/test_swift.py +++ b/openstack_dashboard/test/unit/api/test_swift.py @@ -17,6 +17,8 @@ # under the License. from unittest import mock +from django.test.utils import override_settings + from horizon import exceptions from openstack_dashboard import api @@ -25,7 +27,7 @@ from openstack_dashboard.test import helpers as test @mock.patch('swiftclient.client.Connection') class SwiftApiTests(test.APIMockTestCase): - def test_swift_get_containers(self, mock_swiftclient): + def _test_swift_get_containers(self, mock_swiftclient, full_listing): containers = self.containers.list() cont_data = [c._apidict for c in containers] swift_api = mock_swiftclient.return_value @@ -36,7 +38,15 @@ class SwiftApiTests(test.APIMockTestCase): self.assertEqual(len(containers), len(conts)) self.assertFalse(more) swift_api.get_account.assert_called_once_with( - limit=1001, marker=None, prefix=None, full_listing=True) + limit=1001, marker=None, prefix=None, + full_listing=full_listing) + + def test_swift_get_containers_default(self, mock_swiftclient): + self._test_swift_get_containers(mock_swiftclient, full_listing=True) + + @override_settings(SWIFT_PANEL_FULL_LISTING=False) + def test_swift_get_containers_full_list_false(self, mock_swiftclient): + self._test_swift_get_containers(mock_swiftclient, full_listing=False) def test_swift_get_container_with_data(self, mock_swiftclient): container = self.containers.first() @@ -136,7 +146,7 @@ class SwiftApiTests(test.APIMockTestCase): swift_api.post_container.assert_called_once_with(container.name, headers=headers) - def test_swift_get_objects(self, mock_swiftclient): + def _test_swift_get_objects(self, mock_swiftclient, full_listing): container = self.containers.first() objects = self.objects.list() @@ -154,7 +164,14 @@ class SwiftApiTests(test.APIMockTestCase): marker=None, prefix=None, delimiter='/', - full_listing=True) + full_listing=full_listing) + + def test_swift_get_objects_default(self, mock_swiftclient): + self._test_swift_get_objects(mock_swiftclient, full_listing=True) + + @override_settings(SWIFT_PANEL_FULL_LISTING=False) + def test_swift_get_objects_full_list_false(self, mock_swiftclient): + self._test_swift_get_objects(mock_swiftclient, full_listing=False) def test_swift_get_object_with_data_non_chunked(self, mock_swiftclient): container = self.containers.first() diff --git a/releasenotes/notes/swift-full-listing-config-3c1e03ba0b741916.yaml b/releasenotes/notes/swift-full-listing-config-3c1e03ba0b741916.yaml new file mode 100644 index 0000000000..0cde555f10 --- /dev/null +++ b/releasenotes/notes/swift-full-listing-config-3c1e03ba0b741916.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Added new configuration option ``SWIFT_PANEL_FULL_LISTING`` that + defaults to ``True``. This configuration option can be set to + ``False`` to prevent Horizon from doing full listing of containers + and objects in the Swift panel that can cause high resource + consumption in Horizon.