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-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=3198c5f228a23ecad9bb3bbfc1f748117d180edf;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) Conflicts: src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py Resolution: tentacle has only the empty-result test case; updated its expected stdout to '' --- diff --git a/src/pybind/mgr/dashboard/services/nvmeof_cli.py b/src/pybind/mgr/dashboard/services/nvmeof_cli.py index 9343ddbb377..e98e541ca90 100644 --- a/src/pybind/mgr/dashboard/services/nvmeof_cli.py +++ b/src/pybind/mgr/dashboard/services/nvmeof_cli.py @@ -135,6 +135,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)) @@ -142,6 +144,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 8b98016065a..ee3298bf4f0 100644 --- a/src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py +++ b/src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py @@ -115,13 +115,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()