From: Kefu Chai Date: Tue, 23 Jun 2026 07:43:28 +0000 (+0800) Subject: mgr/dashboard: skip the table when an nvmeof cli result has no columns X-Git-Tag: v21.1.0~17^2~1 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=4a9d5bdf6fdaa4068c0815361ff471dc42502eea;p=ceph.git mgr/dashboard: skip the table when an nvmeof cli result has no columns The dashboard leaves prettytable unpinned. prettytable commit 2574492 ("Apply some Pylint rules (PLR)", #436) rewrote _stringify_row()'s row_height as `max(_get_size(c)[1] for c in row)`, which raises ValueError("max() iterable argument is empty") on a row with no cells. The change is undocumented and shipped in 3.18.0; get_string() trips on it when a table has a row but no columns. AnnotatedDataTextOutputFormatter builds such a table for an empty result, or one whose only field is status or error_message, so NvmeofCLICommand.call() returns -EINVAL and the command fails. This broke run-tox-mgr-dashboard-py3 once the tox virtualenv picked up prettytable 3.18.0. Return an empty string when there are no columns instead of formatting a degenerate table. Fixes: https://tracker.ceph.com/issues/77589 Signed-off-by: Kefu Chai (cherry picked from commit 9dd7fd0b0a78871f7f71c89a061490f3ffc97605) --- diff --git a/src/pybind/mgr/dashboard/services/nvmeof_cli.py b/src/pybind/mgr/dashboard/services/nvmeof_cli.py index a56e5cde968c..5f454c3c24e9 100644 --- a/src/pybind/mgr/dashboard/services/nvmeof_cli.py +++ b/src/pybind/mgr/dashboard/services/nvmeof_cli.py @@ -204,6 +204,8 @@ class AnnotatedDataTextOutputFormatter(OutputFormatter): def _get_list_text_output(self, data): columns = list(dict.fromkeys([key for obj in data for key in obj.keys()])) + if not columns: + return '' table = self._create_table(columns) for d in data: table.add_row(self._get_row(columns, d)) @@ -211,6 +213,8 @@ class AnnotatedDataTextOutputFormatter(OutputFormatter): def _get_object_text_output(self, data): columns = [k for k in data.keys() if k not in ["status", "error_message"]] + if not columns: + return '' table = self._create_table(columns) table.add_row(self._get_row(columns, data)) return table.get_string() diff --git a/src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py b/src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py index bc75950059ad..690103969dfc 100644 --- a/src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py +++ b/src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py @@ -117,13 +117,7 @@ class TestNvmeofCLICommand: result = NvmeofCLICommand.COMMANDS[sample_command].call(MagicMock(), {}) assert isinstance(result, HandleCommandResult) assert result.retval == 0 - assert result.stdout == ( - "++\n" - "||\n" - "++\n" - "\n" - "++" - ) + assert result.stdout == '' assert result.stderr == '' base_call_return_none_mock.assert_called_once() @@ -788,13 +782,7 @@ class TestNvmeofCLICommandSuccessMessage: ) assert res.retval == 0 assert res.stderr == "" - assert res.stdout == ( - "++\n" - "||\n" - "++\n" - "\n" - "++" - ) + assert res.stdout == '' del NvmeofCLICommand.COMMANDS[test_cmd] assert test_cmd not in NvmeofCLICommand.COMMANDS