]> 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:09:23 +0000 (22:09 +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)

Conflicts:
    src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py
Resolution: tentacle has only the empty-result test case; updated its
expected stdout to ''

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

index 9343ddbb377fb9f3d641d5493d6f1528e5e94a11..e98e541ca90d677b7485b629aeee6f2d6d6c5c0c 100644 (file)
@@ -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()
index 8b98016065ad4815f3a66c783dc8fd72ec858ed5..ee3298bf4f098ce1cab6faf5500458845566a09b 100644 (file)
@@ -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()