From 9ffa0df96d26504aa209f317fe48269a4f41f7f3 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Nov 13 2019 21:04:33 +0000 Subject: Issue 50699 - Add Disk Monitor to CLI and UI Description: Add the disk monitoring to the CLI and UI relates: https://pagure.io/389-ds-base/issue/50699 Reviewed by: spichugi(Thanks!) --- diff --git a/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx b/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx index c401196..26db0ca 100644 --- a/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx +++ b/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx @@ -8,7 +8,7 @@ import { tableCellFormatter, noop } from "patternfly-react"; -import { DSTable } from "../dsTable.jsx"; +import { DSTable, DSShortTable } from "../dsTable.jsx"; import PropTypes from "prop-types"; import "../../css/ds.css"; import { get_date_string } from "../tools.jsx"; @@ -1584,6 +1584,121 @@ class ConflictTable extends React.Component { } } +class DiskTable extends React.Component { + constructor(props) { + super(props); + + this.state = { + rowKey: "mount", + columns: [ + { + property: "mount", + header: { + label: "Disk Partition", + props: { + index: 0, + rowSpan: 1, + colSpan: 1, + sort: true + }, + transforms: [], + formatters: [], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 0 + }, + formatters: [tableCellFormatter] + } + }, + { + property: "size", + header: { + label: "Disk Size", + props: { + index: 1, + rowSpan: 1, + colSpan: 1, + sort: true + }, + transforms: [], + formatters: [], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 1 + }, + formatters: [tableCellFormatter] + } + }, + { + property: "used", + header: { + label: "Used Space", + props: { + index: 2, + rowSpan: 1, + colSpan: 1, + sort: true + }, + transforms: [], + formatters: [], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 2 + }, + formatters: [tableCellFormatter] + } + }, + { + property: "avail", + header: { + label: "Available Space", + props: { + index: 3, + rowSpan: 1, + colSpan: 1, + sort: true + }, + transforms: [], + formatters: [], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 3 + }, + formatters: [tableCellFormatter] + } + }, + + ] + }; + this.getColumns = this.getColumns.bind(this); + } + + getColumns() { + return this.state.columns; + } + + render() { + return ( +
+ +
+ ); + } +} + // Proptypes and defaults LagReportTable.propTypes = { @@ -1681,4 +1796,5 @@ export { AbortCleanALLRUVTable, ConflictTable, GlueTable, + DiskTable, }; diff --git a/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx b/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx index e330f82..b150907 100644 --- a/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx +++ b/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx @@ -3,9 +3,11 @@ import PropTypes from "prop-types"; import "../../css/ds.css"; import { get_date_string, get_date_diff } from "../tools.jsx"; import { - ConnectionTable + ConnectionTable, + DiskTable, } from "./monitorTables.jsx"; import { + Button, Nav, NavItem, TabContent, @@ -66,6 +68,9 @@ export class ServerMonitor extends React.Component {
+ +
+ @@ -191,6 +196,17 @@ export class ServerMonitor extends React.Component { + + + +
diff --git a/src/cockpit/389-console/src/monitor.jsx b/src/cockpit/389-console/src/monitor.jsx index d9d7807..6798dda 100644 --- a/src/cockpit/389-console/src/monitor.jsx +++ b/src/cockpit/389-console/src/monitor.jsx @@ -39,6 +39,7 @@ export class Monitor extends React.Component { snmpData: {}, ldbmData: {}, serverData: {}, + disks: [], loadingMsg: "", notifications: [], disableTree: false, @@ -124,6 +125,8 @@ export class Monitor extends React.Component { this.loadMonitorServer = this.loadMonitorServer.bind(this); this.reloadServer = this.reloadServer.bind(this); this.loadMonitorChaining = this.loadMonitorChaining.bind(this); + this.loadDiskSpace = this.loadDiskSpace.bind(this); + this.reloadDisks = this.reloadDisks.bind(this); // Replication this.loadMonitorReplication = this.loadMonitorReplication.bind(this); this.loadCleanTasks = this.loadCleanTasks.bind(this); @@ -457,7 +460,7 @@ export class Monitor extends React.Component { replicatedSuffixes: config.items, replSuffix: replSuffix, }); - }, this.loadMonitorLDBM()); + }, this.loadDiskSpace()); } loadMonitorLDBM() { @@ -528,7 +531,7 @@ export class Monitor extends React.Component { this.setState({ serverLoading: false, serverData: config.attrs - }); + }, this.reloadDisks()); }); } @@ -548,6 +551,44 @@ export class Monitor extends React.Component { }); } + loadDiskSpace() { + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "monitor", "disk" + ]; + log_cmd("loadDiskSpace", "Load disk space info", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let disks = JSON.parse(content); + for (let disk of disks.items) { + disk.used = disk.used + " (" + disk.percent + "%)"; + } + this.setState({ + disks: disks.items + }); + }, this.loadMonitorLDBM()); + } + + reloadDisks () { + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "monitor", "disk" + ]; + log_cmd("reloadDisks", "Reload disk stats", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let disks = JSON.parse(content); + for (let disk of disks.items) { + disk.used = disk.used + " (" + disk.percent + "%)"; + } + this.setState({ + disks: disks.items, + }); + }); + } + reloadSNMP() { this.setState({ snmpLoading: true @@ -1053,6 +1094,8 @@ export class Monitor extends React.Component { data={this.state.serverData} reload={this.reloadServer} serverId={this.props.serverId} + disks={this.state.disks} + reloadDisks={this.reloadDisks} enableTree={this.enableTree} />; } diff --git a/src/lib389/lib389/cli_conf/monitor.py b/src/lib389/lib389/cli_conf/monitor.py index 87eb75f..ff3712e 100644 --- a/src/lib389/lib389/cli_conf/monitor.py +++ b/src/lib389/lib389/cli_conf/monitor.py @@ -7,9 +7,11 @@ # See LICENSE for details. # --- END COPYRIGHT BLOCK --- -from lib389.monitor import (Monitor, MonitorLDBM, MonitorSNMP) +import json +from lib389.monitor import (Monitor, MonitorLDBM, MonitorSNMP, MonitorDiskSpace) from lib389.chaining import (ChainingLinks) from lib389.backend import Backends +from lib389.utils import convert_bytes def _format_status(log, mtype, json=False): @@ -63,9 +65,39 @@ def chaining_monitor(inst, basedn, log, args): for link in links.list(): link_monitor = link.get_monitor() _format_status(log, link_monitor, args.json) - # Inejct a new line for now ... see https://pagure.io/389-ds-base/issue/50189 + # Inject a new line for now ... see https://pagure.io/389-ds-base/issue/50189 log.info("") +def disk_monitor(inst, basedn, log, args): + disk_space_mon = MonitorDiskSpace(inst) + disks = disk_space_mon.get_disks() + disk_list = [] + for disk in disks: + # partition="/" size="52576092160" used="25305038848" available="27271053312" use%="48" + parts = disk.split() + mount = parts[0].split('=')[1].strip('"') + disk_size = convert_bytes(parts[1].split('=')[1].strip('"')) + used = convert_bytes(parts[2].split('=')[1].strip('"')) + avail = convert_bytes(parts[3].split('=')[1].strip('"')) + percent = parts[4].split('=')[1].strip('"') + if args.json: + disk_list.append({ + 'mount': mount, + 'size': disk_size, + 'used': used, + 'avail': avail, + 'percent': percent + }) + else: + log.info("Partition: " + mount) + log.info("Size: " + disk_size) + log.info("Used Space: " + used) + log.info("Available Space: " + avail) + log.info("Percentage Used: " + percent + "%\n") + + if args.json: + log.info(json.dumps({"type": "list", "items": disk_list})) + def create_parser(subparsers): monitor_parser = subparsers.add_parser('monitor', help="Monitor the state of the instance") @@ -87,3 +119,6 @@ def create_parser(subparsers): chaining_parser = subcommands.add_parser('chaining', help="Monitor database chaining statistics") chaining_parser.add_argument('backend', nargs='?', help="Optional name of the chaining backend to monitor") chaining_parser.set_defaults(func=chaining_monitor) + + disk_parser = subcommands.add_parser('disk', help="Disk space statistics. All values are in bytes") + disk_parser.set_defaults(func=disk_monitor) diff --git a/src/lib389/lib389/utils.py b/src/lib389/lib389/utils.py index c271d86..a0b063a 100644 --- a/src/lib389/lib389/utils.py +++ b/src/lib389/lib389/utils.py @@ -39,6 +39,8 @@ import six import shlex import operator import subprocess +import math +from packaging.version import LegacyVersion from socket import getfqdn from ldapurl import LDAPUrl from contextlib import closing @@ -1301,3 +1303,14 @@ def display_log_value(attr, value, hide_sensitive=True): def display_log_data(data, hide_sensitive=True): # Take a dict and mask all the sensitive data return {a: display_log_value(a, v, hide_sensitive) for a, v in data.items()} + + +def convert_bytes(bytes): + bytes = int(bytes) + if bytes == 0: + return "0 B" + size_name = ["B", "KB", "MB", "GB", "TB", "PB"] + i = int(math.floor(math.log(bytes, 1024))) + pow = math.pow(1024, i) + siz = round(bytes / pow, 2) + return "{} {}".format(siz, size_name[i])