Skip to content

Commit 80d0761

Browse files
committed
Merge branch 'issue2252' into develop
2 parents 1153d18 + 81f6ec0 commit 80d0761

File tree

16 files changed

+686
-18
lines changed

16 files changed

+686
-18
lines changed

conf/glances.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,14 @@ port_default_gateway=True
461461
#web_4_url=https://blog.nicolargo.com/nonexist
462462
#web_4_description=Intranet
463463

464+
[vms]
465+
disable=False
466+
# Define the maximum VMs size name (default is 20 chars)
467+
max_name_size=20
468+
# By default, Glances only display running VMs
469+
# Set the following key to True to display all VMs
470+
all=False
471+
464472
[containers]
465473
disable=False
466474
# Only show specific containers (comma-separated list of container name or regular expression)

glances/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ def init_ui_mode(self, args):
692692
disable(args, 'alert')
693693
disable(args, 'amps')
694694
disable(args, 'containers')
695+
disable(args, 'vms')
695696

696697
# Manage full quicklook option
697698
if getattr(args, 'full_quicklook', False):

glances/outputs/glances_curses.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class _GlancesCurses:
8484
'T': {'switch': 'network_sum'},
8585
'u': {'sort_key': 'username'},
8686
'U': {'switch': 'network_cumul'},
87+
'V': {'switch': 'disable_vms'},
8788
'w': {'handler': '_handle_clean_logs'},
8889
'W': {'switch': 'disable_wifi'},
8990
'x': {'handler': '_handle_clean_critical_logs'},
@@ -124,7 +125,7 @@ class _GlancesCurses:
124125
_left_sidebar_max_width = 34
125126

126127
# Define right sidebar
127-
_right_sidebar = ['containers', 'processcount', 'amps', 'processlist', 'alert']
128+
_right_sidebar = ['vms', 'containers', 'processcount', 'amps', 'processlist', 'alert']
128129

129130
def __init__(self, config=None, args=None):
130131
# Init

glances/outputs/static/css/style.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ body {
145145
padding: 0px 5px 0px 5px;
146146
white-space: nowrap;
147147
}
148+
149+
#vms-plugin .table-cell {
150+
padding: 0px 10px 0px 10px;
151+
white-space: nowrap;
152+
}
153+
148154
#containers-plugin .table-cell {
149155
padding: 0px 10px 0px 10px;
150156
white-space: nowrap;

glances/outputs/static/js/App.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@
7979
</div>
8080
</div>
8181
<div class="col-sm-18">
82+
<glances-plugin-vms
83+
v-if="!args.disable_vms"
84+
:data="data"
85+
></glances-plugin-vms>
8286
<glances-plugin-containers
8387
v-if="!args.disable_containers"
8488
:data="data"
@@ -126,6 +130,7 @@ import GlancesPluginSmart from './components/plugin-smart.vue';
126130
import GlancesPluginSensors from './components/plugin-sensors.vue';
127131
import GlancesPluginSystem from './components/plugin-system.vue';
128132
import GlancesPluginUptime from './components/plugin-uptime.vue';
133+
import GlancesPluginVms from './components/plugin-vms.vue';
129134
import GlancesPluginWifi from './components/plugin-wifi.vue';
130135
131136
import uiconfig from './uiconfig.json';
@@ -159,6 +164,7 @@ export default {
159164
GlancesPluginSmart,
160165
GlancesPluginSystem,
161166
GlancesPluginUptime,
167+
GlancesPluginVms,
162168
GlancesPluginWifi
163169
},
164170
data() {
@@ -374,6 +380,11 @@ export default {
374380
this.store.args.disable_ports = !this.store.args.disable_ports;
375381
});
376382
383+
// V => Enable/disable VMs stats
384+
hotkeys('shift+V', () => {
385+
this.store.args.disable_vms = !this.store.args.disable_vms;
386+
});
387+
377388
// 'W' > Enable/Disable Wifi plugin
378389
hotkeys('shift+W', () => {
379390
this.store.args.disable_wifi = !this.store.args.disable_wifi;

glances/outputs/static/js/components/help.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@
6060
<div class="divTableCell">
6161
{{ help.sort_io_rate }}
6262
</div>
63+
<div class="divTableCell">
64+
{{ help.show_hide_vms }}
65+
</div>
6366
<div class="divTableCell">
6467
{{ help.show_hide_containers }}
6568
</div>
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<template>
2+
<section id="vms-plugin" class="plugin" v-if="vms.length">
3+
<span class="title">VMs</span>
4+
{{ vms.length }} sorted by {{ sorter.getColumnLabel(sorter.column) }}
5+
<div class="table">
6+
<div class="table-row">
7+
<div class="table-cell text-left" v-show="showEngine">Engine</div>
8+
<div
9+
class="table-cell text-left"
10+
:class="['sortable', sorter.column === 'name' && 'sort']"
11+
@click="args.sort_processes_key = 'name'"
12+
>
13+
Name
14+
</div>
15+
<div class="table-cell">Status</div>
16+
<div class="table-cell">Core</div>
17+
<div
18+
class="table-cell"
19+
:class="['sortable', sorter.column === 'memory_usage' && 'sort']"
20+
@click="args.sort_processes_key = 'memory_usage'"
21+
>
22+
MEM
23+
</div>
24+
<div class="table-cell text-left">/MAX</div>
25+
<div
26+
class="table-cell"
27+
:class="['sortable', sorter.column === 'load_1min' && 'sort']"
28+
@click="args.sort_processes_key = 'load_1min'"
29+
>
30+
LOAD 1/5/15min
31+
</div>
32+
<div class="table-cell text-right">Release</div>
33+
</div>
34+
<div
35+
class="table-row"
36+
v-for="(vm, vmId) in vms"
37+
:key="vmId"
38+
>
39+
<div class="table-cell text-left" v-show="showEngine">{{ vm.engine }}</div>
40+
<div class="table-cell text-left">{{ vm.name }}</div>
41+
<div class="table-cell" :class="vm.status == 'stopped' ? 'careful' : 'ok'">
42+
{{ vm.status }}
43+
</div>
44+
<div class="table-cell">
45+
{{ $filters.number(vm.cpu_count, 1) }}
46+
</div>
47+
<div class="table-cell">
48+
{{ $filters.bytes(vm.memory_usage) }}
49+
</div>
50+
<div class="table-cell text-left">
51+
/{{ $filters.bytes(vm.memory_total) }}
52+
</div>
53+
<div class="table-cell">
54+
{{ $filters.number(vm.load_1min) }}/{{ $filters.number(vm.load_5min) }}/{{ $filters.number(vm.load_15min) }}
55+
</div>
56+
<div class="table-cell text-right">
57+
{{ vm.release }}
58+
</div>
59+
</div>
60+
</div>
61+
</section>
62+
</template>
63+
64+
<script>
65+
import { orderBy } from 'lodash';
66+
import { store } from '../store.js';
67+
68+
export default {
69+
props: {
70+
data: {
71+
type: Object
72+
}
73+
},
74+
data() {
75+
return {
76+
store,
77+
sorter: undefined
78+
};
79+
},
80+
computed: {
81+
args() {
82+
return this.store.args || {};
83+
},
84+
sortProcessesKey() {
85+
return this.args.sort_processes_key;
86+
},
87+
stats() {
88+
return this.data.stats['vms'];
89+
},
90+
views() {
91+
return this.data.views['vms'];
92+
},
93+
vms() {
94+
const { sorter } = this;
95+
const vms = (this.stats || []).map(
96+
(vmData) => {
97+
return {
98+
'id': vmData.id,
99+
'name': vmData.name,
100+
'status': vmData.status != undefined ? vmData.status : '?',
101+
'cpu_count': vmData.cpu_count != undefined ? vmData.cpu_count : '?',
102+
'memory_usage': vmData.memory_usage != undefined ? vmData.memory_usage : '?',
103+
'memory_total': vmData.memory_total != undefined ? vmData.memory_total : '?',
104+
'load_1min': vmData.load_1min != undefined ? vmData.load_1min : '?',
105+
'load_5min': vmData.load_5min != undefined ? vmData.load_5min : '?',
106+
'load_15min': vmData.load_15min != undefined ? vmData.load_15min : '?',
107+
'release': vmData.release,
108+
'image': vmData.image,
109+
'engine': vmData.engine,
110+
'engine_version': vmData.engine_version,
111+
};
112+
}
113+
);
114+
return orderBy(
115+
vms,
116+
[sorter.column].reduce((retval, col) => {
117+
if (col === 'memory_usage') {
118+
col = ['memory_usage'];
119+
}
120+
return retval.concat(col);
121+
}, []),
122+
[sorter.isReverseColumn(sorter.column) ? 'desc' : 'asc']
123+
);
124+
},
125+
showEngine() {
126+
return this.views.show_engine_name;
127+
},
128+
},
129+
watch: {
130+
sortProcessesKey: {
131+
immediate: true,
132+
handler(sortProcessesKey) {
133+
const sortable = ['load_1min', 'memory_usage', 'name'];
134+
function isReverseColumn(column) {
135+
return !['name'].includes(column);
136+
}
137+
function getColumnLabel(value) {
138+
const labels = {
139+
load_1min: 'load',
140+
memory_usage: 'memory consumption',
141+
name: 'VM name',
142+
None: 'None'
143+
};
144+
return labels[value] || value;
145+
}
146+
if (!sortProcessesKey || sortable.includes(sortProcessesKey)) {
147+
this.sorter = {
148+
column: this.args.sort_processes_key || 'load_1min',
149+
auto: !this.args.sort_processes_key,
150+
isReverseColumn,
151+
getColumnLabel
152+
};
153+
}
154+
}
155+
}
156+
}
157+
};
158+
</script>

glances/outputs/static/public/glances.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

glances/plugins/containers/__init__.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ def update(self) -> List[Dict]:
227227
for engine, watcher in iteritems(self.watchers):
228228
version, containers = watcher.update(all_tag=self._all_tag())
229229
for container in containers:
230-
container["engine"] = 'docker'
230+
container["engine"] = engine
231231
stats.extend(containers)
232232

233233
# Sort and update the stats
@@ -307,6 +307,9 @@ def msg_curse(self, args=None, max_width: Optional[int] = None) -> List[str]:
307307
ret.append(self.curse_add_line(msg))
308308
msg = f' sorted by {sort_for_human[self.sort_key]}'
309309
ret.append(self.curse_add_line(msg))
310+
if not self.views['show_engine_name']:
311+
msg = f' (served by {self.stats[0].get("engine", "")})'
312+
ret.append(self.curse_add_line(msg))
310313
ret.append(self.curse_new_line())
311314
# Header
312315
ret.append(self.curse_new_line())
@@ -430,25 +433,29 @@ def msg_curse(self, args=None, max_width: Optional[int] = None) -> List[str]:
430433

431434
@staticmethod
432435
def container_alert(status: str) -> str:
433-
"""Analyse the container status."""
436+
"""Analyse the container status.
437+
One of created, restarting, running, removing, paused, exited, or dead
438+
"""
434439
if status == 'running':
435440
return 'OK'
436-
if status == 'exited':
437-
return 'WARNING'
438441
if status == 'dead':
439-
return 'CRITICAL'
440-
return 'CAREFUL'
442+
return 'ERROR'
443+
if status in ['created', 'restarting', 'exited']:
444+
return 'WARNING'
445+
return 'INFO'
441446

442447

443448
def sort_docker_stats(stats: List[Dict[str, Any]]) -> Tuple[str, List[Dict[str, Any]]]:
444-
# Sort Docker stats using the same function than processes
445-
sort_by = glances_processes.sort_key
446-
sort_by_secondary = 'memory_usage'
447-
if sort_by == 'memory_percent':
449+
# Make VM sort related to process sort
450+
if glances_processes.sort_key == 'memory_percent':
448451
sort_by = 'memory_usage'
449452
sort_by_secondary = 'cpu_percent'
450-
elif sort_by in ['username', 'io_counters', 'cpu_times']:
453+
elif glances_processes.sort_key == 'name':
454+
sort_by = 'name'
455+
sort_by_secondary = 'cpu_percent'
456+
else:
451457
sort_by = 'cpu_percent'
458+
sort_by_secondary = 'memory_usage'
452459

453460
# Sort docker stats
454461
sort_stats_processes(

glances/plugins/containers/engines/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
#
2+
# This file is part of Glances.
3+
#
4+
# SPDX-FileCopyrightText: 2024 Nicolas Hennion <nicolas@nicolargo.com>
5+
#
6+
# SPDX-License-Identifier: LGPL-3.0-only
7+
#
8+
19
from typing import Any, Dict, Protocol, Tuple
210

311

0 commit comments

Comments
 (0)