]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: skip the table when an nvmeof cli result has no columns
authorKefu Chai <k.chai@proxmox.com>
Tue, 23 Jun 2026 07:43:28 +0000 (15:43 +0800)
committerKefu Chai <k.chai@proxmox.com>
Wed, 24 Jun 2026 14:04:19 +0000 (22:04 +0800)
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 <k.chai@proxmox.com>
(cherry picked from commit 9dd7fd0b0a78871f7f71c89a061490f3ffc97605)

src/pybind/mgr/dashboard/services/nvmeof_cli.py
src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py

index a56e5cde968c209e8bc367150c61d374fa6541d2..5f454c3c24e9fb5b8e8941ee328e0455a8bfa869 100644 (file)
@@ -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()
index bc75950059adfca33b22d353df534d7f53bf2a65..690103969dfcaf25fbbed06023745bf7e6e62a79 100644 (file)
@@ -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