Merge pull request #2919 from ariel-anieli/refactor-plugin-model-msg-curse

Refactorized `glances.plugins.containers.PluginModel.msg_curse()`
pull/2924/head
Nicolas Hennion 2024-08-18 09:36:25 +02:00 committed by GitHub
commit 504cf0613f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 174 additions and 97 deletions

View File

@ -9,6 +9,7 @@
"""Containers plugin.""" """Containers plugin."""
from copy import deepcopy from copy import deepcopy
from functools import partial, reduce
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
from glances.globals import iteritems, itervalues from glances.globals import iteritems, itervalues
@ -290,17 +291,7 @@ class PluginModel(GlancesPluginModel):
return True return True
def msg_curse(self, args=None, max_width: Optional[int] = None) -> List[str]: def build_title(self, ret):
"""Return the dict to display in the curse interface."""
# Init the return message
ret = []
# Only process if stats exist (and non null) and display plugin enable...
if not self.stats or len(self.stats) == 0 or self.is_disabled():
return ret
# Build the string message
# Title
msg = '{}'.format('CONTAINERS') msg = '{}'.format('CONTAINERS')
ret.append(self.curse_add_line(msg, "TITLE")) ret.append(self.curse_add_line(msg, "TITLE"))
msg = f' {len(self.stats)}' msg = f' {len(self.stats)}'
@ -311,72 +302,98 @@ class PluginModel(GlancesPluginModel):
msg = f' (served by {self.stats[0].get("engine", "")})' msg = f' (served by {self.stats[0].get("engine", "")})'
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
# Header return ret
ret.append(self.curse_new_line())
# Get the maximum containers name
# Max size is configurable. See feature request #1723.
name_max_width = min(
self.config.get_int_value('containers', 'max_name_size', default=20) if self.config is not None else 20,
len(max(self.stats, key=lambda x: len(x['name']))['name']),
)
def maybe_add_engine_name_or_pod_line(self, ret):
if self.views['show_engine_name']: if self.views['show_engine_name']:
msg = ' {:{width}}'.format('Engine', width=6) ret = self.add_msg_to_line(ret, ' {:{width}}'.format('Engine', width=6))
ret.append(self.curse_add_line(msg))
if self.views['show_pod_name']: if self.views['show_pod_name']:
msg = ' {:{width}}'.format('Pod', width=12) ret = self.add_msg_to_line(ret, ' {:{width}}'.format('Pod', width=12))
ret.append(self.curse_add_line(msg))
msg = ' {:{width}}'.format('Name', width=name_max_width)
ret.append(self.curse_add_line(msg, 'SORT' if self.sort_key == 'name' else 'DEFAULT'))
msg = '{:>10}'.format('Status')
ret.append(self.curse_add_line(msg))
msg = '{:>10}'.format('Uptime')
ret.append(self.curse_add_line(msg))
msg = '{:>6}'.format('CPU%')
ret.append(self.curse_add_line(msg, 'SORT' if self.sort_key == 'cpu_percent' else 'DEFAULT'))
msg = '{:>7}'.format('MEM')
ret.append(self.curse_add_line(msg, 'SORT' if self.sort_key == 'memory_usage' else 'DEFAULT'))
msg = '/{:<7}'.format('MAX')
ret.append(self.curse_add_line(msg))
msg = '{:>7}'.format('IOR/s')
ret.append(self.curse_add_line(msg))
msg = ' {:<7}'.format('IOW/s')
ret.append(self.curse_add_line(msg))
msg = '{:>7}'.format('Rx/s')
ret.append(self.curse_add_line(msg))
msg = ' {:<7}'.format('Tx/s')
ret.append(self.curse_add_line(msg))
msg = ' {:8}'.format('Command')
ret.append(self.curse_add_line(msg))
# Data return ret
for container in self.stats:
def maybe_add_engine_name_or_pod_name(self, ret, container):
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
if self.views['show_engine_name']: if self.views['show_engine_name']:
ret.append(self.curse_add_line(' {:{width}}'.format(container["engine"], width=6))) ret.append(self.curse_add_line(' {:{width}}'.format(container["engine"], width=6)))
if self.views['show_pod_name']: if self.views['show_pod_name']:
ret.append(self.curse_add_line(' {:{width}}'.format(container.get("pod_id", "-"), width=12))) ret.append(self.curse_add_line(' {:{width}}'.format(container.get("pod_id", "-"), width=12)))
# Name
return ret
def build_container_name(self, name_max_width):
def build_for_this_max_length(ret, container):
ret.append( ret.append(
self.curse_add_line(' {:{width}}'.format(container['name'][:name_max_width], width=name_max_width)) self.curse_add_line(' {:{width}}'.format(container['name'][:name_max_width], width=name_max_width))
) )
# Status
return ret
return build_for_this_max_length
def build_header(self, ret, name_max_width):
ret.append(self.curse_new_line())
ret = self.maybe_add_engine_name_or_pod_line(ret)
msg = ' {:{width}}'.format('Name', width=name_max_width)
ret.append(self.curse_add_line(msg, 'SORT' if self.sort_key == 'name' else 'DEFAULT'))
msgs = ['{:>10}'.format('Status'), '{:>10}'.format('Uptime')]
ret = reduce(self.add_msg_to_line, msgs, ret)
msg = '{:>6}'.format('CPU%')
ret.append(self.curse_add_line(msg, 'SORT' if self.sort_key == 'cpu_percent' else 'DEFAULT'))
msg = '{:>7}'.format('MEM')
ret.append(self.curse_add_line(msg, 'SORT' if self.sort_key == 'memory_usage' else 'DEFAULT'))
msgs = [
'/{:<7}'.format('MAX'),
'{:>7}'.format('IOR/s'),
' {:<7}'.format('IOW/s'),
'{:>7}'.format('Rx/s'),
' {:<7}'.format('Tx/s'),
' {:8}'.format('Command'),
]
return reduce(self.add_msg_to_line, msgs, ret)
def add_msg_to_line(self, ret, msg):
ret.append(self.curse_add_line(msg))
return ret
def get_max_of_container_names(self):
return min(
self.config.get_int_value('containers', 'max_name_size', default=20) if self.config is not None else 20,
len(max(self.stats, key=lambda x: len(x['name']))['name']),
)
def build_status_name(self, ret, container):
status = self.container_alert(container['status']) status = self.container_alert(container['status'])
msg = '{:>10}'.format(container['status'][0:10]) msg = '{:>10}'.format(container['status'][0:10])
ret.append(self.curse_add_line(msg, status)) ret.append(self.curse_add_line(msg, status))
# Uptime
return ret
def build_uptime_line(self, ret, container):
if container['uptime']: if container['uptime']:
msg = '{:>10}'.format(container['uptime']) msg = '{:>10}'.format(container['uptime'])
else: else:
msg = '{:>10}'.format('_') msg = '{:>10}'.format('_')
ret.append(self.curse_add_line(msg))
# CPU return self.add_msg_to_line(ret, msg)
def build_cpu_line(self, ret, container):
try: try:
msg = '{:>6.1f}'.format(container['cpu']['total']) msg = '{:>6.1f}'.format(container['cpu']['total'])
except (KeyError, TypeError): except (KeyError, TypeError):
msg = '{:>6}'.format('_') msg = '{:>6}'.format('_')
ret.append(self.curse_add_line(msg, self.get_views(item=container['name'], key='cpu', option='decoration'))) ret.append(self.curse_add_line(msg, self.get_views(item=container['name'], key='cpu', option='decoration')))
# MEM
return ret
def build_memory_line(self, ret, container):
try: try:
msg = '{:>7}'.format(self.auto_unit(self.memory_usage_no_cache(container['memory']))) msg = '{:>7}'.format(self.auto_unit(self.memory_usage_no_cache(container['memory'])))
except KeyError: except KeyError:
@ -387,7 +404,10 @@ class PluginModel(GlancesPluginModel):
except (KeyError, TypeError): except (KeyError, TypeError):
msg = '/{:<7}'.format('_') msg = '/{:<7}'.format('_')
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
# IO R/W
return ret
def build_io_line(self, ret, container):
unit = 'B' unit = 'B'
try: try:
value = self.auto_unit(int(container['io_rx'])) + unit value = self.auto_unit(int(container['io_rx'])) + unit
@ -401,7 +421,11 @@ class PluginModel(GlancesPluginModel):
except (KeyError, TypeError): except (KeyError, TypeError):
msg = ' {:<7}'.format('_') msg = ' {:<7}'.format('_')
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
# NET RX/TX
return ret
def build_net_line(self, args):
def build_with_this_args(ret, container):
if args.byte: if args.byte:
# Bytes per second (for dummy) # Bytes per second (for dummy)
to_bit = 1 to_bit = 1
@ -422,7 +446,12 @@ class PluginModel(GlancesPluginModel):
except (KeyError, TypeError): except (KeyError, TypeError):
msg = ' {:<7}'.format('_') msg = ' {:<7}'.format('_')
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
# Command
return ret
return build_with_this_args
def build_cmd_line(self, ret, container):
if container['command'] is not None: if container['command'] is not None:
msg = ' {}'.format(container['command']) msg = ' {}'.format(container['command'])
else: else:
@ -431,6 +460,54 @@ class PluginModel(GlancesPluginModel):
return ret return ret
def msg_curse(self, args=None, max_width: Optional[int] = None) -> List[str]:
"""Return the dict to display in the curse interface."""
# Init the return message
init = []
# Only process if stats exist (and non null) and display plugin enable...
conditions = [not self.stats, len(self.stats) == 0, self.is_disabled()]
if any(conditions):
return init
# Build the string message
# Get the maximum containers name
# Max size is configurable. See feature request #1723.
name_max_width = self.get_max_of_container_names()
steps = [
self.build_title,
partial(self.build_header, name_max_width=name_max_width),
self.build_data_line(name_max_width, args),
]
return reduce(lambda ret, step: step(ret), steps, init)
def build_data_line(self, name_max_width, args):
def build_for_this_params(ret):
build_data_with_params = self.build_container_data(name_max_width, args)
return reduce(build_data_with_params, self.stats, ret)
return build_for_this_params
def build_container_data(self, name_max_width, args):
def build_with_this_params(ret, container):
steps = [
self.maybe_add_engine_name_or_pod_name,
self.build_container_name(name_max_width),
self.build_status_name,
self.build_uptime_line,
self.build_cpu_line,
self.build_memory_line,
self.build_io_line,
self.build_net_line(args),
self.build_cmd_line,
]
return reduce(lambda ret, step: step(ret, container), steps, ret)
return build_with_this_params
@staticmethod @staticmethod
def container_alert(status: str) -> str: def container_alert(status: str) -> str:
"""Analyse the container status. """Analyse the container status.