1313import tempfile
1414from urllib .parse import urlparse
1515
16+ from io import StringIO
17+
1618from rich .console import Console
19+ from rich .table import Table
1720
1821from badfish .helpers import get_now
1922from badfish .helpers .parser import parse_arguments
@@ -99,6 +102,15 @@ def __init__(
99102 self .vendor = None
100103 self .console = _console if _console is not None else Console ()
101104 self ._progress_disabled = _progress_disabled
105+ # Tables are useful only in the same conditions as progress bars: TTY,
106+ # single-host, no structured output, no log file.
107+ self ._use_tables = not _progress_disabled
108+
109+ def _log_table (self , table ):
110+ buf = StringIO ()
111+ tmp = Console (file = buf , highlight = False , force_terminal = self .console .is_terminal , no_color = self .console .no_color )
112+ tmp .print (table )
113+ self .logger .info (buf .getvalue ().rstrip ("\n " ), extra = {"is_table" : True })
102114
103115 async def __aenter__ (self ):
104116 await self .init ()
@@ -1295,12 +1307,21 @@ async def check_boot(self, _interfaces_path):
12951307 return True
12961308 else :
12971309 self .logger .warning ("Current boot order does not match any of the given." )
1298- self .logger .info ("Current boot order:" )
1310+
1311+ self .logger .info ("Current boot order:" )
1312+ if self ._use_tables :
1313+ table = Table (show_header = True , header_style = "bold" )
1314+ table .add_column ("#" , justify = "right" )
1315+ table .add_column ("Device" )
1316+ table .add_column ("Status" )
1317+ for device in sorted (self .boot_devices , key = lambda x : x ["Index" ]):
1318+ status = "Enabled" if device ["Enabled" ] else "Disabled"
1319+ table .add_row (str (int (device ["Index" ]) + 1 ), device ["Name" ], status )
1320+ self ._log_table (table )
12991321 else :
1300- self .logger .info ("Current boot order:" )
1301- for device in sorted (self .boot_devices , key = lambda x : x ["Index" ]):
1302- enabled = "" if device ["Enabled" ] else " (DISABLED)"
1303- self .logger .info ("%s: %s%s" % (int (device ["Index" ]) + 1 , device ["Name" ], enabled ))
1322+ for device in sorted (self .boot_devices , key = lambda x : x ["Index" ]):
1323+ enabled = "" if device ["Enabled" ] else " (DISABLED)"
1324+ self .logger .info ("%s: %s%s" % (int (device ["Index" ]) + 1 , device ["Name" ], enabled ))
13041325 return True
13051326
13061327 async def check_device (self , device ):
@@ -1387,6 +1408,8 @@ async def get_firmware_inventory(self):
13871408 if "Installed" in a :
13881409 installed_devices .append (a )
13891410
1411+ _SKIP = {"odata" , "Description" , "Oem" }
1412+ rows = []
13901413 for device in installed_devices :
13911414 self .logger .debug ("Getting device info for %s" % device )
13921415 _uri = "%s/UpdateService/FirmwareInventory/%s" % (self .root_uri , device )
@@ -1397,11 +1420,32 @@ async def get_firmware_inventory(self):
13971420
13981421 raw = await _response .text ("utf-8" , "ignore" )
13991422 data = json .loads (raw .strip ())
1400- for info in data .items ():
1401- if "Id" == info [0 ]:
1402- self .logger .info ("%s:" % info [1 ])
1403- if "odata" not in info [0 ] and "Description" not in info [0 ] and "Oem" not in info [0 ]:
1404- self .logger .info (" %s: %s" % (info [0 ], info [1 ]))
1423+ row = {k : v for k , v in data .items () if not any (s in k for s in _SKIP )}
1424+ rows .append (row )
1425+
1426+ if rows :
1427+ if self ._use_tables :
1428+ cols = ["Id" , "Name" , "Version" , "Manufacturer" , "Status" ]
1429+ table = Table (show_header = True , header_style = "bold" )
1430+ for col in cols :
1431+ table .add_column (col )
1432+ for row in rows :
1433+ status = row .get ("Status" )
1434+ if isinstance (status , dict ):
1435+ status = status .get ("State" , "" )
1436+ table .add_row (
1437+ str (row .get ("Id" , "" )),
1438+ str (row .get ("Name" , "" )),
1439+ str (row .get ("Version" , "" )),
1440+ str (row .get ("Manufacturer" , "" )),
1441+ str (status or "" ),
1442+ )
1443+ self ._log_table (table )
1444+ else :
1445+ for row in rows :
1446+ self .logger .info ("%s:" % row .get ("Id" , "" ))
1447+ for k , v in row .items ():
1448+ self .logger .info (" %s: %s" % (k , v ))
14051449
14061450 async def get_host_type_boot_device (self , host_type , _interfaces_path ):
14071451 if _interfaces_path :
@@ -1844,22 +1888,49 @@ async def list_interfaces(self):
18441888 self .logger .error ("Server does not support this functionality" )
18451889 return False
18461890
1847- for interface , properties in data .items ():
1848- self .logger .info (f"{ interface } :" )
1849- for key , value in properties .items ():
1850- if key == "SupportedLinkCapabilities" :
1851- speed_key = "LinkSpeedMbps"
1852- speed = value [0 ].get (speed_key )
1853- if speed :
1854- self .logger .info (f" { speed_key } : { speed } " )
1855- elif key == "Status" :
1856- health_key = "Health"
1857- health = value .get (health_key )
1858- if health :
1859- self .logger .info (f" { health_key } : { health } " )
1860- else :
1861- self .logger .info (f" { key } : { value } " )
1891+ if self ._use_tables :
1892+ cols = {}
1893+ for interface , properties in data .items ():
1894+ for key , value in properties .items ():
1895+ if key == "SupportedLinkCapabilities" :
1896+ cols ["LinkSpeedMbps" ] = True
1897+ elif key == "Status" :
1898+ cols ["Health" ] = True
1899+ else :
1900+ cols [key ] = True
1901+
1902+ table = Table (show_header = True , header_style = "bold" )
1903+ table .add_column ("Interface" )
1904+ for col in cols :
1905+ table .add_column (col )
1906+
1907+ for interface , properties in data .items ():
1908+ row_vals = {"Interface" : interface }
1909+ for key , value in properties .items ():
1910+ if key == "SupportedLinkCapabilities" :
1911+ caps = value [0 ] if isinstance (value , list ) and value else {}
1912+ row_vals ["LinkSpeedMbps" ] = str (caps .get ("LinkSpeedMbps" , "" ))
1913+ elif key == "Status" :
1914+ row_vals ["Health" ] = str (value .get ("Health" , "" ))
1915+ else :
1916+ row_vals [key ] = str (value )
1917+ table .add_row (* [row_vals .get (c , "" ) for c in ["Interface" ] + list (cols )])
18621918
1919+ self ._log_table (table )
1920+ else :
1921+ for interface , properties in data .items ():
1922+ self .logger .info (f"{ interface } :" )
1923+ for key , value in properties .items ():
1924+ if key == "SupportedLinkCapabilities" :
1925+ speed = value [0 ].get ("LinkSpeedMbps" ) if value else None
1926+ if speed :
1927+ self .logger .info (f" LinkSpeedMbps: { speed } " )
1928+ elif key == "Status" :
1929+ health = value .get ("Health" )
1930+ if health :
1931+ self .logger .info (f" Health: { health } " )
1932+ else :
1933+ self .logger .info (f" { key } : { value } " )
18631934 return True
18641935
18651936 async def get_processor_summary (self ):
@@ -2129,15 +2200,33 @@ async def list_processors(self):
21292200 data = await self .get_processor_summary ()
21302201
21312202 self .logger .info ("Processor Summary:" )
2132- for _key , _value in data .items ():
2133- self .logger .info (f" { _key } : { _value } " )
2203+ if self ._use_tables :
2204+ summary = Table (show_header = True , header_style = "bold" )
2205+ summary .add_column ("Property" )
2206+ summary .add_column ("Value" )
2207+ for _key , _value in data .items ():
2208+ summary .add_row (_key , str (_value ))
2209+ self ._log_table (summary )
2210+ else :
2211+ for _key , _value in data .items ():
2212+ self .logger .info (f" { _key } : { _value } " )
21342213
21352214 processor_data = await self .get_processor_details ()
21362215
2137- for _processor , _properties in processor_data .items ():
2138- self .logger .info (f"{ _processor } :" )
2139- for _key , _value in _properties .items ():
2140- self .logger .info (f" { _key } : { _value } " )
2216+ if self ._use_tables :
2217+ detail = Table (show_header = True , header_style = "bold" )
2218+ detail .add_column ("Processor" )
2219+ all_keys = dict .fromkeys (k for props in processor_data .values () for k in props )
2220+ for col in all_keys :
2221+ detail .add_column (col )
2222+ for _processor , _properties in processor_data .items ():
2223+ detail .add_row (_processor , * [str (_properties .get (k , "" )) for k in all_keys ])
2224+ self ._log_table (detail )
2225+ else :
2226+ for _processor , _properties in processor_data .items ():
2227+ self .logger .info (f"{ _processor } :" )
2228+ for _key , _value in _properties .items ():
2229+ self .logger .info (f" { _key } : { _value } " )
21412230
21422231 return True
21432232
@@ -2148,33 +2237,68 @@ async def list_gpu(self):
21482237 summary = await self .get_gpu_summary (gpu_responses )
21492238
21502239 self .logger .info ("GPU Summary:" )
2151- for _key , _value in summary .items ():
2152- self .logger .info (f" Model: { _key } (Count: { _value } )" )
2153-
2154- self .logger .info ("Current GPU's on host:" )
2240+ if self ._use_tables :
2241+ summary_table = Table (show_header = True , header_style = "bold" )
2242+ summary_table .add_column ("Model" )
2243+ summary_table .add_column ("Count" , justify = "right" )
2244+ for _key , _value in summary .items ():
2245+ summary_table .add_row (_key , str (_value ))
2246+ self ._log_table (summary_table )
2247+ else :
2248+ for _key , _value in summary .items ():
2249+ self .logger .info (f" Model: { _key } (Count: { _value } )" )
21552250
21562251 gpu_data = await self .get_gpu_details (gpu_responses )
21572252
2158- for _gpu , _properties in gpu_data .items ():
2159- self .logger .info (f" { _gpu } :" )
2160- for _key , _value in _properties .items ():
2161- self .logger .info (f" { _key } : { _value } " )
2253+ self .logger .info ("Current GPU's on host:" )
2254+ if self ._use_tables :
2255+ detail = Table (show_header = True , header_style = "bold" )
2256+ detail .add_column ("GPU" )
2257+ all_keys = dict .fromkeys (k for props in gpu_data .values () for k in props )
2258+ for col in all_keys :
2259+ detail .add_column (col )
2260+ for _gpu , _properties in gpu_data .items ():
2261+ detail .add_row (_gpu , * [str (_properties .get (k , "" )) for k in all_keys ])
2262+ self ._log_table (detail )
2263+ else :
2264+ for _gpu , _properties in gpu_data .items ():
2265+ self .logger .info (f" { _gpu } :" )
2266+ for _key , _value in _properties .items ():
2267+ self .logger .info (f" { _key } : { _value } " )
21622268
21632269 return True
21642270
21652271 async def list_memory (self ):
21662272 data = await self .get_memory_summary ()
21672273
21682274 self .logger .info ("Memory Summary:" )
2169- for _key , _value in data .items ():
2170- self .logger .info (f" { _key } : { _value } " )
2275+ if self ._use_tables :
2276+ summary = Table (show_header = True , header_style = "bold" )
2277+ summary .add_column ("Property" )
2278+ summary .add_column ("Value" )
2279+ for _key , _value in data .items ():
2280+ summary .add_row (_key , str (_value ))
2281+ self ._log_table (summary )
2282+ else :
2283+ for _key , _value in data .items ():
2284+ self .logger .info (f" { _key } : { _value } " )
21712285
21722286 memory_data = await self .get_memory_details ()
21732287
2174- for _memory , _properties in memory_data .items ():
2175- self .logger .info (f"{ _memory } :" )
2176- for _key , _value in _properties .items ():
2177- self .logger .info (f" { _key } : { _value } " )
2288+ if self ._use_tables :
2289+ detail = Table (show_header = True , header_style = "bold" )
2290+ detail .add_column ("DIMM" )
2291+ all_keys = dict .fromkeys (k for props in memory_data .values () for k in props )
2292+ for col in all_keys :
2293+ detail .add_column (col )
2294+ for _memory , _properties in memory_data .items ():
2295+ detail .add_row (_memory , * [str (_properties .get (k , "" )) for k in all_keys ])
2296+ self ._log_table (detail )
2297+ else :
2298+ for _memory , _properties in memory_data .items ():
2299+ self .logger .info (f"{ _memory } :" )
2300+ for _key , _value in _properties .items ():
2301+ self .logger .info (f" { _key } : { _value } " )
21782302
21792303 return True
21802304
@@ -2984,9 +3108,8 @@ def main(argv=None):
29843108 multi_host = True if host_list else False
29853109 result = True
29863110 output = _args ["output" ]
2987- bfl = BadfishLogger (_args ["verbose" ], multi_host , _args ["log" ], output )
2988-
29893111 console = Console ()
3112+ bfl = BadfishLogger (_args ["verbose" ], multi_host , _args ["log" ], output , console = console )
29903113 progress_disabled = bool (output ) or multi_host or bool (_args ["log" ]) or not console .is_terminal
29913114
29923115 try :
0 commit comments