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,97 +302,130 @@ 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))
return ret
def maybe_add_engine_name_or_pod_name(self, ret, container):
ret.append(self.curse_new_line())
if self.views['show_engine_name']:
ret.append(self.curse_add_line(' {:{width}}'.format(container["engine"], width=6)))
if self.views['show_pod_name']:
ret.append(self.curse_add_line(' {:{width}}'.format(container.get("pod_id", "-"), width=12)))
return ret
def build_container_name(self, name_max_width):
def build_for_this_max_length(ret, container):
ret.append(
self.curse_add_line(' {:{width}}'.format(container['name'][:name_max_width], width=name_max_width))
)
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) msg = ' {:{width}}'.format('Name', width=name_max_width)
ret.append(self.curse_add_line(msg, 'SORT' if self.sort_key == 'name' else 'DEFAULT')) 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)) msgs = ['{:>10}'.format('Status'), '{:>10}'.format('Uptime')]
msg = '{:>10}'.format('Uptime') ret = reduce(self.add_msg_to_line, msgs, ret)
ret.append(self.curse_add_line(msg))
msg = '{:>6}'.format('CPU%') msg = '{:>6}'.format('CPU%')
ret.append(self.curse_add_line(msg, 'SORT' if self.sort_key == 'cpu_percent' else 'DEFAULT')) ret.append(self.curse_add_line(msg, 'SORT' if self.sort_key == 'cpu_percent' else 'DEFAULT'))
msg = '{:>7}'.format('MEM') msg = '{:>7}'.format('MEM')
ret.append(self.curse_add_line(msg, 'SORT' if self.sort_key == 'memory_usage' else 'DEFAULT')) 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)) msgs = [
msg = '{:>7}'.format('IOR/s') '/{:<7}'.format('MAX'),
ret.append(self.curse_add_line(msg)) '{:>7}'.format('IOR/s'),
msg = ' {:<7}'.format('IOW/s') ' {:<7}'.format('IOW/s'),
ret.append(self.curse_add_line(msg)) '{:>7}'.format('Rx/s'),
msg = '{:>7}'.format('Rx/s') ' {:<7}'.format('Tx/s'),
ret.append(self.curse_add_line(msg)) ' {:8}'.format('Command'),
msg = ' {:<7}'.format('Tx/s') ]
ret.append(self.curse_add_line(msg))
msg = ' {: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)) ret.append(self.curse_add_line(msg))
# Data return ret
for container in self.stats:
ret.append(self.curse_new_line()) def get_max_of_container_names(self):
if self.views['show_engine_name']: return min(
ret.append(self.curse_add_line(' {:{width}}'.format(container["engine"], width=6))) self.config.get_int_value('containers', 'max_name_size', default=20) if self.config is not None else 20,
if self.views['show_pod_name']: len(max(self.stats, key=lambda x: len(x['name']))['name']),
ret.append(self.curse_add_line(' {:{width}}'.format(container.get("pod_id", "-"), width=12))) )
# Name
ret.append( def build_status_name(self, ret, container):
self.curse_add_line(' {:{width}}'.format(container['name'][:name_max_width], width=name_max_width)) status = self.container_alert(container['status'])
) msg = '{:>10}'.format(container['status'][0:10])
# Status ret.append(self.curse_add_line(msg, status))
status = self.container_alert(container['status'])
msg = '{:>10}'.format(container['status'][0:10]) return ret
ret.append(self.curse_add_line(msg, status))
# Uptime 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)
try:
msg = '{:>6.1f}'.format(container['cpu']['total']) def build_cpu_line(self, ret, container):
except (KeyError, TypeError): try:
msg = '{:>6}'.format('_') msg = '{:>6.1f}'.format(container['cpu']['total'])
ret.append(self.curse_add_line(msg, self.get_views(item=container['name'], key='cpu', option='decoration'))) except (KeyError, TypeError):
# MEM msg = '{:>6}'.format('_')
try: ret.append(self.curse_add_line(msg, self.get_views(item=container['name'], key='cpu', option='decoration')))
msg = '{:>7}'.format(self.auto_unit(self.memory_usage_no_cache(container['memory'])))
except KeyError: return ret
msg = '{:>7}'.format('_')
ret.append(self.curse_add_line(msg, self.get_views(item=container['name'], key='mem', option='decoration'))) def build_memory_line(self, ret, container):
try: try:
msg = '/{:<7}'.format(self.auto_unit(container['memory']['limit'])) msg = '{:>7}'.format(self.auto_unit(self.memory_usage_no_cache(container['memory'])))
except (KeyError, TypeError): except KeyError:
msg = '/{:<7}'.format('_') msg = '{:>7}'.format('_')
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg, self.get_views(item=container['name'], key='mem', option='decoration')))
# IO R/W try:
unit = 'B' msg = '/{:<7}'.format(self.auto_unit(container['memory']['limit']))
try: except (KeyError, TypeError):
value = self.auto_unit(int(container['io_rx'])) + unit msg = '/{:<7}'.format('_')
msg = f'{value:>7}' ret.append(self.curse_add_line(msg))
except (KeyError, TypeError):
msg = '{:>7}'.format('_') return ret
ret.append(self.curse_add_line(msg))
try: def build_io_line(self, ret, container):
value = self.auto_unit(int(container['io_wx'])) + unit unit = 'B'
msg = f' {value:<7}' try:
except (KeyError, TypeError): value = self.auto_unit(int(container['io_rx'])) + unit
msg = ' {:<7}'.format('_') msg = f'{value:>7}'
ret.append(self.curse_add_line(msg)) except (KeyError, TypeError):
# NET RX/TX msg = '{:>7}'.format('_')
ret.append(self.curse_add_line(msg))
try:
value = self.auto_unit(int(container['io_wx'])) + unit
msg = f' {value:<7}'
except (KeyError, TypeError):
msg = ' {:<7}'.format('_')
ret.append(self.curse_add_line(msg))
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,15 +446,68 @@ 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
if container['command'] is not None: return ret
msg = ' {}'.format(container['command'])
else: return build_with_this_args
msg = ' {}'.format('_')
ret.append(self.curse_add_line(msg, splittable=True)) def build_cmd_line(self, ret, container):
if container['command'] is not None:
msg = ' {}'.format(container['command'])
else:
msg = ' {}'.format('_')
ret.append(self.curse_add_line(msg, splittable=True))
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.