Merge pull request #2145 from fr4nc0is/2138-added-sortable-columns-docker-plugin

added sortable columns in docker plugin
pull/2153/head
Nicolas Hennion 2022-10-04 10:41:26 +02:00 committed by GitHub
commit a4067cd856
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 178 additions and 95 deletions

View File

@ -234,6 +234,41 @@ export default {
},
methods: {
setupHotKeys() {
// a => Sort processes/docker automatically
hotkeys('a', () => {
this.store.args.sort_processes_key = null;
});
// c => Sort processes/docker by CPU%
hotkeys('c', () => {
this.store.args.sort_processes_key = 'cpu_percent';
});
// m => Sort processes/docker by MEM%
hotkeys('m', () => {
this.store.args.sort_processes_key = 'memory_percent';
});
// u => Sort processes/docker by user
hotkeys('u', () => {
this.store.args.sort_processes_key = 'username';
});
// p => Sort processes/docker by name
hotkeys('p', () => {
this.store.args.sort_processes_key = 'name';
});
// i => Sort processes/docker by I/O rate
hotkeys('i', () => {
this.store.args.sort_processes_key = 'io_counters';
});
// t => Sort processes/docker by time
hotkeys('t', () => {
this.store.args.sort_processes_key = 'timemillis';
});
// A => Enable/disable AMPs
hotkeys('shift+A', () => {
this.store.args.disable_amps = !this.store.args.disable_amps;

View File

@ -1,14 +1,32 @@
<template>
<section id="containers-plugin" class="plugin" v-if="containers.length">
<span class="title">CONTAINERS</span>
{{ containers.length }} (served by Docker {{ version }})
{{ containers.length }} sorted by {{ sorter.getColumnLabel(sorter.column) }}
<div class="table">
<div class="table-row">
<div class="table-cell text-left">Name</div>
<div
class="table-cell text-left"
:class="['sortable', sorter.column === 'name' && 'sort']"
@click="args.sort_processes_key = 'name'"
>
Name
</div>
<div class="table-cell">Status</div>
<div class="table-cell">Uptime</div>
<div class="table-cell">CPU%</div>
<div class="table-cell">MEM</div>
<div
class="table-cell"
:class="['sortable', sorter.column === 'cpu_percent' && 'sort']"
@click="args.sort_processes_key = 'cpu_percent'"
>
CPU%
</div>
<div
class="table-cell"
:class="['sortable', sorter.column === 'memory_percent' && 'sort']"
@click="args.sort_processes_key = 'memory_percent'"
>
MEM
</div>
<div class="table-cell">/MAX</div>
<div class="table-cell">IOR/s</div>
<div class="table-cell">IOW/s</div>
@ -29,10 +47,10 @@
{{ container.uptime }}
</div>
<div class="table-cell">
{{ $filters.number(container.cpu, 1) }}
{{ $filters.number(container.cpu_percent, 1) }}
</div>
<div class="table-cell">
{{ $filters.bytes(container.memory) }}
{{ $filters.bytes(container.memory_usage) }}
</div>
<div class="table-cell">
{{ $filters.bytes(container.limit) }}
@ -58,26 +76,42 @@
</template>
<script>
import { orderBy } from 'lodash';
import { store } from '../store.js';
export default {
props: {
data: {
type: Object
}
},
data() {
return {
store,
sorter: undefined
};
},
computed: {
args() {
return this.store.args || {};
},
sortProcessesKey() {
return this.args.sort_processes_key;
},
stats() {
return this.data.stats['docker'];
},
containers() {
return (this.stats.containers || []).map((containerData) => {
const { sorter } = this;
const containers = (this.stats.containers || []).map((containerData) => {
// prettier-ignore
return {
'id': containerData.Id,
'name': containerData.name,
'status': containerData.Status,
'uptime': containerData.Uptime,
'cpu': containerData.cpu.total,
'memory': containerData.memory.usage != undefined ? containerData.memory.usage : '?',
'cpu_percent': containerData.cpu.total,
'memory_usage': containerData.memory.usage != undefined ? containerData.memory.usage : '?',
'limit': containerData.memory.limit != undefined ? containerData.memory.limit : '?',
'ior': containerData.io.ior != undefined ? containerData.io.ior : '?',
'iow': containerData.io.iow != undefined ? containerData.io.iow : '?',
@ -87,11 +121,48 @@ export default {
'net_time_since_update': containerData.network.time_since_update,
'command': containerData.Command.join(' '),
'image': containerData.Image
};
};
});
},
version() {
return (this.stats['version'] || {})['Version'];
return orderBy(
containers,
[sorter.column].reduce((retval, col) => {
if (col === 'memory_percent') {
col = ['memory_usage'];
}
return retval.concat(col);
}, []),
[sorter.isReverseColumn(sorter.column) ? 'desc' : 'asc']
);
}
},
watch: {
sortProcessesKey: {
immediate: true,
handler(sortProcessesKey) {
const sortable = ['cpu_percent', 'memory_percent', 'name'];
function isReverseColumn(column) {
return !['name'].includes(column);
}
function getColumnLabel(value) {
const labels = {
io_counters: 'disk IO',
cpu_percent: 'CPU consumption',
memory_usage: 'memory consumption',
cpu_times: 'uptime',
name: 'container name',
None: 'None'
};
return labels[value] || value;
}
if (!sortProcessesKey || sortable.includes(sortProcessesKey)) {
this.sorter = {
column: this.args.sort_processes_key || 'cpu_percent',
auto: !this.args.sort_processes_key,
isReverseColumn,
getColumnLabel
};
}
}
}
}
};

View File

@ -10,7 +10,7 @@
<span v-if="port.status == 'null'">Scanning</span>
<span v-else-if="port.status == 'false'">Timeout</span>
<span v-else-if="port.status == 'true'">Open</span>
<span v-else> {{ $filters.number(numberport.status * 1000.0, 0) }}ms </span>
<span v-else>{{ $filters.number(port.status * 1000.0, 0) }}ms</span>
</div>
<div :class="getWebDecoration(port)" class="table-cell" v-if="port.url">
<span v-if="port.status == 'null'">Scanning</span>

View File

@ -10,13 +10,12 @@
<glances-plugin-processlist
:sorter="sorter"
:data="data"
@update:sorter="sorter.column = $event"
@update:sorter="args.sort_processes_key = $event"
></glances-plugin-processlist>
</div>
</template>
<script>
import hotkeys from 'hotkeys-js';
import { store } from '../store.js';
import GlancesPluginAmps from './plugin-amps.vue';
import GlancesPluginProcesscount from './plugin-processcount.vue';
@ -36,83 +35,56 @@ export default {
data() {
return {
store,
sorter: {
column: 'cpu_percent',
auto: true,
isReverseColumn(column) {
return !(column === 'username' || column === 'name');
},
getColumnLabel(column) {
if (column === 'io_read' || column === 'io_write') {
return 'io_counters';
} else {
return column;
}
}
}
sorter: undefined
};
},
computed: {
args() {
return this.store.args || {};
},
sortProcessesKey() {
return this.args.sort_processes_key;
}
},
methods: {
setupHotKeys() {
// a => Sort processes automatically
hotkeys('a', () => {
this.sorter.column = 'cpu_percent';
this.sorter.auto = true;
});
// c => Sort processes by CPU%
hotkeys('c', () => {
this.sorter.column = 'cpu_percent';
this.sorter.auto = false;
});
// m => Sort processes by MEM%
hotkeys('m', () => {
this.sorter.column = 'memory_percent';
this.sorter.auto = false;
});
// u => Sort processes by user
hotkeys('u', () => {
this.sorter.column = 'username';
this.sorter.auto = false;
});
// p => Sort processes by name
hotkeys('p', () => {
this.sorter.column = 'name';
this.sorter.auto = false;
});
// i => Sort processes by I/O rate
hotkeys('i', () => {
this.sorter.column = ['io_read', 'io_write'];
this.sorter.auto = false;
});
// t => Sort processes by time
hotkeys('t', () => {
this.sorter.column = 'timemillis';
this.sorter.auto = false;
});
watch: {
sortProcessesKey: {
immediate: true,
handler(sortProcessesKey) {
const sortable = [
'cpu_percent',
'memory_percent',
'username',
'timemillis',
'num_threads',
'io_counters',
'name'
];
function isReverseColumn(column) {
return !['username', 'name'].includes(column);
}
function getColumnLabel(value) {
const labels = {
io_counters: 'disk IO',
cpu_percent: 'CPU consumption',
memory_percent: 'memory consumption',
cpu_times: 'process time',
username: 'user name',
name: 'process name',
timemillis: 'process time',
None: 'None'
};
return labels[value] || value;
}
if (!sortProcessesKey || sortable.includes(sortProcessesKey)) {
this.sorter = {
column: this.args.sort_processes_key || 'cpu_percent',
auto: !this.args.sort_processes_key,
isReverseColumn,
getColumnLabel
};
}
}
}
},
mounted() {
this.setupHotKeys();
},
beforeUnmount() {
hotkeys.unbind('a');
hotkeys.unbind('c');
hotkeys.unbind('m');
hotkeys.unbind('u');
hotkeys.unbind('p');
hotkeys.unbind('i');
hotkeys.unbind('t');
}
};
</script>

View File

@ -5,7 +5,7 @@
<span>{{ running }} run,</span>
<span>{{ sleeping }} slp,</span>
<span>{{ stopped }} oth</span>
<span class="title">sorted {{ sorter.auto ? 'automatically' : '' }}</span>
<span class="title">{{ sorter.auto ? 'sorted automatically' : 'sorted' }}</span>
<span>by {{ sorter.getColumnLabel(sorter.column) }}</span>
</section>
</template>

View File

@ -46,16 +46,16 @@
<div
v-show="ioReadWritePresent"
class="table-cell hidden-xs hidden-sm"
:class="['sortable', sorter.column === 'io_read' && 'sort']"
@click="$emit('update:sorter', 'io_read')"
:class="['sortable', sorter.column === 'io_counters' && 'sort']"
@click="$emit('update:sorter', 'io_counters')"
>
IOR/s
</div>
<div
v-show="ioReadWritePresent"
class="table-cell text-left hidden-xs hidden-sm"
:class="['sortable', sorter.column === 'io_write' && 'sort']"
@click="$emit('update:sorter', 'io_write')"
:class="['sortable', sorter.column === 'io_counters' && 'sort']"
@click="$emit('update:sorter', 'io_counters')"
>
IOW/s
</div>
@ -215,7 +215,12 @@ export default {
return orderBy(
processes,
[sorter.column],
[sorter.column].reduce((retval, col) => {
if (col === 'io_counters') {
col = ['io_read', 'io_write']
}
return retval.concat(col);
}, []),
[sorter.isReverseColumn(sorter.column) ? 'desc' : 'asc']
).slice(0, this.limit);
},

File diff suppressed because one or more lines are too long