From 1d13ff252cd1f9b672bd6399dd556c57bb4eace7 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Apr 12 2019 20:23:26 +0000 Subject: Ticket 50291 - Add monitor tab functionality to Cockpit UI Description: Added the backend functionality to the monitoring tab. Also returned all dsconf errors as json objects so the UI could display friendly error messages https://pagure.io/389-ds-base/issue/50291 Reviewed by: spichugi(Thanks!) (cherry picked from commit ab94fc12e2dedf21c7784609600d60b9999e1ce4) --- diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css index 6af009c..e0ceeb8 100644 --- a/src/cockpit/389-console/src/css/ds.css +++ b/src/cockpit/389-console/src/css/ds.css @@ -106,7 +106,7 @@ } .ds-chart-right { - margin-left: 90px; + margin-left: 65px; } .ds-chart-left { @@ -233,6 +233,25 @@ line-height: 1; } +.ds-refresh { + background-color: #0088ce; + background-image: linear-gradient(to bottom,#39a5dc 0,#0088ce 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff39a5dc', endColorstr='#ff0088ce', GradientType=0); + border-color: #00659c; + color: #fff; + padding: 3px; + box-shadow: 0 2px 3px rgba(3,3,3,.1); + border-radius: 50%; + border: 1px solid transparent; + font-size: 13px !important; +} + +.ds-refresh:hover { + color: DarkGray; + background-color: white; + background-image: none; +} + .dataTables_wrapper { padding: 0px !important; margin-bottom: 10px !important; @@ -241,7 +260,7 @@ td { white-space: normal; word-wrap: break-word !important; - max-width: 400px !important; + max-width: 200px !important; } .ds-hr { @@ -339,6 +358,12 @@ td { padding-left: 5px; } +.ds-input-auto-good { + width: 100%; + border-color: green; + padding-left: 5px; +} + .ds-input-right { text-align: right; } @@ -385,6 +410,10 @@ td { max-width: 600px; } +.ds-lag-report { + min-width: 800px !important; +} + .ds-repl-mgr { max-width: 600px !important; } @@ -578,10 +607,7 @@ td { } .ds-db-table { - background-color: white !important; - padding: 0px; - border: 1px solid #909090; - clear: both; + border: 1px solid #d1d1d1; word-wrap: break-word !important; text-align: center; } @@ -599,7 +625,6 @@ td { .ds-table-header th { text-align: center !important; - background-color: white; } .ds-plugin-table { @@ -1058,13 +1083,13 @@ textarea { vertical-align: top; width: 315px; height: 80px; - resize: none; word-wrap: break-word !important; line-height: 1; } .ds-agmt-textarea { width: 100%; + font-family: monospace !important; } .ds-logarea { @@ -1072,23 +1097,14 @@ textarea { padding-top: 5px; vertical-align: top; width: 100% !important; - height: 600px !important; + height: 500px; max-height: 600px !important; - resize: none; word-wrap: break-word !important; line-height: 1; font-family: monospace !important; overflow-y: scroll; } -/* Removes dotted outline border of the text */ -select { - text-shadow: 0 0 0 #000 !important; - max-height: 200px !important; - line-height: 1; - padding: 5px; -} - option { color: #181818; } @@ -1137,24 +1153,28 @@ option { float: left; } -.ds-footer { - background-color: #e5e5e5 !important; +.ds-footer{ + background-color: #f5f5f5 !important; margin-left: -25px; padding: 10px; position: fixed; bottom: 0; width: 100%; height: 50px; - border-top: 1px solid #999 !important; + border-top: 1px solid #e2e2e2 !important; } -.ds-modal-footer { +.modal-footer { background-color: #f5f5f5 !important; padding: 10px; bottom: 0; width: 100%; height: 50px; - border-top: 1px solid #999 !important; + border-top: .5px solid #e2e2e2 !important; +} + +.modal-header { + border-bottom: .5px solid #e2e2e2 !important; } .ds-nav-bar { @@ -1226,6 +1246,13 @@ option { margin-top: 10px; } +.ds-margin-top-sm { + margin-top: 9px; +} +.ds-margin-top-med { + margin-top: 15px !important; +} + .ds-margin-top-lg { margin-top: 20px !important; } @@ -1406,6 +1433,10 @@ option { margin-left: 40px !important; } +.ds-margin-left-piechart { + margin-left: 130px !important; +} + .ds-nested-modal { margin-top: 100px; margin-left: 100px; @@ -1466,10 +1497,6 @@ option { position: relative; } -tbody:nth-child(even) { - background: #f1f1f1; -} - .ds-toolbar { bottom: 10px; } @@ -1546,8 +1573,7 @@ tbody:nth-child(even) { } .ds-table-pagination { - background-color: transparent !important; - border: 1px solid #909090; + border: 1px solid #d1d1d1; } .ds-vlv-label { @@ -1587,12 +1613,16 @@ tbody:nth-child(even) { margin-bottom: 15px; } +.ds-header { + font-size: 16px; +} + .ds-no-padding () { padding: 0 !imporant; } .alert { - max-width: 600px; + max-width: 750px; } .ds-confirm { @@ -1622,3 +1652,11 @@ tbody:nth-child(even) { border-color: #bee1f4; display: block; } + +input { + padding-left: 5px !important; +} + +.ds-width-auto { + width: 100%; +} diff --git a/src/cockpit/389-console/src/database.jsx b/src/cockpit/389-console/src/database.jsx index cbcb126..e5adf79 100644 --- a/src/cockpit/389-console/src/database.jsx +++ b/src/cockpit/389-console/src/database.jsx @@ -185,9 +185,10 @@ export class Database extends React.Component { }), this.setState({configUpdated: 0})); }) .fail(err => { + let errMsg = JSON.parse(err); this.addNotification( "error", - `Error loading database configuration - ${err}` + `Error loading database configuration - ${errMsg.desc}` ); }); } @@ -271,9 +272,10 @@ export class Database extends React.Component { ), this.loadAvailableControls()); }) .fail(err => { + let errMsg = JSON.parse(err); this.addNotification( "error", - `Error loading default chaining configuration - ${err}` + `Error loading default chaining configuration - ${errMsg.desc}` ); this.setState({ loading: false @@ -430,9 +432,10 @@ export class Database extends React.Component { }); }) .fail(err => { + let errMsg = JSON.parse(err); this.addNotification( "error", - `Error getting chaining link configuration - ${err}` + `Error getting chaining link configuration - ${errMsg.desc}` ); }); } @@ -615,9 +618,10 @@ export class Database extends React.Component { this.loadSuffixTree(false); }) .fail(err => { + let errMsg = JSON.parse(err); this.addNotification( "error", - `Error creating suffix - ${err}` + `Error creating suffix - ${errMsg.desc}` ); this.closeSuffixModal(); }); @@ -774,9 +778,10 @@ export class Database extends React.Component { }); }) .fail(err => { + let errMsg = JSON.parse(err); this.addNotification( "error", - `Error loading indexes for ${suffix} - ${err}` + `Error loading indexes for ${suffix} - ${errMsg.desc}` ); }); } @@ -816,7 +821,7 @@ export class Database extends React.Component { "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", "backend", "suffix", "get", suffix ]; - log_cmd("loadSuffixConfig", "Load suffix config", cmd); + log_cmd("loadSuffix", "Load suffix config", cmd); cockpit .spawn(cmd, { superuser: true, err: "message" }) .done(content => { @@ -928,9 +933,10 @@ export class Database extends React.Component { }); }) .fail(err => { + let errMsg = JSON.parse(err); this.addNotification( "error", - `Error loading indexes for ${suffix} - ${err}` + `Error loading indexes for ${suffix} - ${errMsg.desc}` ); this.setState({ suffixLoading: false @@ -999,9 +1005,10 @@ export class Database extends React.Component { }); }) .fail(err => { + let errMsg = JSON.parse(err); this.addNotification( "error", - `Failed to get attributes - ${err}` + `Failed to get attributes - ${errMsg.desc}` ); }); } diff --git a/src/cockpit/389-console/src/ds.js b/src/cockpit/389-console/src/ds.js index 8d5e3dc..32a97dc 100644 --- a/src/cockpit/389-console/src/ds.js +++ b/src/cockpit/389-console/src/ds.js @@ -13,7 +13,7 @@ var db_page_loaded = 1; var repl_page_loaded = 0; var plugin_page_loaded = 1; var schema_page_loaded = 0; -var monitor_page_loaded = 0; +var monitor_page_loaded = 1; var config_loaded = 0; // objects to store original values (used for comparing what changed when saving @@ -118,18 +118,50 @@ function sort_list (sel) { function get_date_string (timestamp) { // Convert DS timestamp to a friendly string: 20180921142257Z -> 10/21/2018, 2:22:57 PM - var year = timestamp.substr(0,4); - var month = timestamp.substr(4,2); - var day = timestamp.substr(6,2); - var hour = timestamp.substr(8,2); - var minute = timestamp.substr(10,2); - var sec = timestamp.substr(12,2); - var date = new Date(parseInt(year), parseInt(month), parseInt(day), + let year = timestamp.substr(0,4); + let month = timestamp.substr(4,2); + let day = timestamp.substr(6,2); + let hour = timestamp.substr(8,2); + let minute = timestamp.substr(10,2); + let sec = timestamp.substr(12,2); + let date = new Date(parseInt(year), parseInt(month), parseInt(day), parseInt(hour), parseInt(minute), parseInt(sec)); return date.toLocaleString(); } +function get_date_diff(start, end) { + // Get the start up date + let year = start.substr(0,4); + let month = start.substr(4,2); + let day = start.substr(6,2); + let hour = start.substr(8,2); + let minute = start.substr(10,2); + let sec = start.substr(12,2); + let startDate = new Date(parseInt(year), parseInt(month), parseInt(day), + parseInt(hour), parseInt(minute), parseInt(sec)); + + // Get the servers current date + year = end.substr(0,4); + month = end.substr(4,2); + day = end.substr(6,2); + hour = end.substr(8,2); + minute = end.substr(10,2); + sec = end.substr(12,2); + let currDate = new Date(parseInt(year), parseInt(month), parseInt(day), + parseInt(hour), parseInt(minute), parseInt(sec)); + + // Generate pretty elapsed time string + let seconds = Math.floor((startDate - (currDate))/1000); + let minutes = Math.floor(seconds/60); + let hours = Math.floor(minutes/60); + let days = Math.floor(hours/24); + hours = hours-(days*24); + minutes = minutes-(days*24*60)-(hours*60); + seconds = seconds-(days*24*60*60)-(hours*60*60)-(minutes*60); + + return("${days} days, ${hours} hours, ${minutes} minutes, and ${seconds} seconds"); +} function set_no_insts () { @@ -437,4 +469,8 @@ $(window.document).ready(function() { $(".all-pages").hide(); $("#database-content").show(); }); + $("#monitor-tab").on("click", function() { + $(".all-pages").hide(); + $("#monitor-content").show(); + }); }); diff --git a/src/cockpit/389-console/src/index.es6 b/src/cockpit/389-console/src/index.es6 index 404cbb0..0483752 100644 --- a/src/cockpit/389-console/src/index.es6 +++ b/src/cockpit/389-console/src/index.es6 @@ -2,6 +2,7 @@ import React from "react"; import ReactDOM from "react-dom"; import { Plugins } from "./plugins.jsx"; import { Database } from "./database.jsx"; +import { Monitor } from "./monitor.jsx"; var serverIdElem; @@ -15,19 +16,25 @@ function renderReactDOM(clear) { .value.replace("slapd-", ""); } let d = new Date(); - let n = d.getTime(); // might not be needed MARK + let tabKey = d.getTime(); // Plugins Tab ReactDOM.render( - , + , document.getElementById("plugins") ); // Database tab ReactDOM.render( - , + , document.getElementById("database") ); + + // Monitor tab + ReactDOM.render( + , + document.getElementById("monitor") + ); } // We have to create the wrappers because this is no simple way diff --git a/src/cockpit/389-console/src/index.html b/src/cockpit/389-console/src/index.html index 96586d1..7c5dbf8 100644 --- a/src/cockpit/389-console/src/index.html +++ b/src/cockpit/389-console/src/index.html @@ -23,7 +23,6 @@ - @@ -117,7 +116,7 @@
  • Configuration
  • Agreements
  • Winsync Agreements
  • -
  • CleanAllRUV Tasks
  • +
  • Replication Tasks
  • @@ -142,26 +141,10 @@ -
  • + Monitoring - -
  • @@ -281,7 +264,7 @@

    Reloading schema files...

    - - @@ -382,7 +365,7 @@ - - - diff --git a/src/cockpit/389-console/src/lib/database/attrEncryption.jsx b/src/cockpit/389-console/src/lib/database/attrEncryption.jsx index 6965d72..9bc86bc 100644 --- a/src/cockpit/389-console/src/lib/database/attrEncryption.jsx +++ b/src/cockpit/389-console/src/lib/database/attrEncryption.jsx @@ -74,10 +74,11 @@ export class AttrEncryption extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Failed to delete encrypted attribute - ${err}` + `Failed to delete encrypted attribute - ${errMsg.desc}` ); }); } @@ -98,10 +99,11 @@ export class AttrEncryption extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Failure deleting encrypted attribute - ${err}` + `Failure deleting encrypted attribute - ${errMsg.desc}` ); }); } diff --git a/src/cockpit/389-console/src/lib/database/backups.jsx b/src/cockpit/389-console/src/lib/database/backups.jsx index 6ddcc44..065e8a1 100644 --- a/src/cockpit/389-console/src/lib/database/backups.jsx +++ b/src/cockpit/389-console/src/lib/database/backups.jsx @@ -251,10 +251,11 @@ export class Backups extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.closeLDIFSpinningModal(); this.props.addNotification( "error", - `Failure importing LDIF - ${err}` + `Failure importing LDIF - ${errMsg.desc}` ); }); } @@ -277,11 +278,12 @@ export class Backups extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(); this.closeLDIFDeleteSpinningModal(); this.props.addNotification( "error", - `Failure deleting LDIF file - ${err}` + `Failure deleting LDIF file - ${errMsg.desc}` ); }); } @@ -312,11 +314,12 @@ export class Backups extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(); this.closeBackupModal(); this.props.addNotification( "error", - `Failure backing up server - ${err}` + `Failure backing up server - ${errMsg.desc}` ); }); } @@ -339,10 +342,11 @@ export class Backups extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.closeRestoreSpinningModal(); this.props.addNotification( "error", - `Failure restoring up server - ${err}` + `Failure restoring up server - ${errMsg.desc}` ); }); } @@ -366,11 +370,12 @@ export class Backups extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(); this.closeDelBackupSpinningModal(); this.props.addNotification( "error", - `Failure deleting backup - ${err}` + `Failure deleting backup - ${errMsg.desc}` ); }); } @@ -432,11 +437,12 @@ export class Backups extends React.Component { }); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(); this.closeExportModal(); this.props.addNotification( "error", - `Error exporting database - ${err}` + `Error exporting database - ${errMsg.desc}` ); this.setState({ showExportModal: false, diff --git a/src/cockpit/389-console/src/lib/database/chaining.jsx b/src/cockpit/389-console/src/lib/database/chaining.jsx index cf507b3..f20fca6 100644 --- a/src/cockpit/389-console/src/lib/database/chaining.jsx +++ b/src/cockpit/389-console/src/lib/database/chaining.jsx @@ -195,10 +195,11 @@ export class ChainingDatabaseConfig extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(); this.props.addNotification( "error", - `Error updating chaining configuration - ${err}` + `Error updating chaining configuration - ${errMsg.desc}` ); }); } @@ -255,10 +256,11 @@ export class ChainingDatabaseConfig extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(); this.props.addNotification( "error", - `Error updating chaining controls - ${err}` + `Error updating chaining controls - ${errMsg.desc}` ); }); } @@ -298,10 +300,11 @@ export class ChainingDatabaseConfig extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(); this.props.addNotification( "error", - `Error removing chaining controls - ${err}` + `Error removing chaining controls - ${errMsg.desc}` ); }); } @@ -370,10 +373,11 @@ export class ChainingDatabaseConfig extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(); this.props.addNotification( "error", - `Error updating chaining components - ${err}` + `Error updating chaining components - ${errMsg.desc}` ); }); } @@ -400,10 +404,11 @@ export class ChainingDatabaseConfig extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(); this.props.addNotification( "error", - `Error removing chaining components - ${err}` + `Error removing chaining components - ${errMsg.desc}` ); }); } @@ -832,10 +837,11 @@ export class ChainingConfig extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Failed to update link configuration - ${err}` + `Failed to update link configuration - ${errMsg.desc}` ); }); } @@ -857,10 +863,11 @@ export class ChainingConfig extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.loadSuffixTree(true); this.props.addNotification( "error", - `Failed to delete database link - ${err}` + `Failed to delete database link - ${errMsg.desc}` ); }); } @@ -1160,7 +1167,7 @@ export class ChainControlsModal extends React.Component { - + diff --git a/src/cockpit/389-console/src/lib/database/indexes.jsx b/src/cockpit/389-console/src/lib/database/indexes.jsx index 34c246b..f8e157d 100644 --- a/src/cockpit/389-console/src/lib/database/indexes.jsx +++ b/src/cockpit/389-console/src/lib/database/indexes.jsx @@ -132,17 +132,19 @@ export class SuffixIndexes extends React.Component { } }) .fail(err => { + let errMsg = JSON.parse(err); this.props.addNotification( "error", - `Failed to get attributes - ${err}` + `Failed to get attributes - ${errMsg.desc}` ); }); }); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.addNotification( "error", - `Failed to get matching rules - ${err}` + `Failed to get matching rules - ${errMsg.desc}` ); }); } @@ -242,12 +244,12 @@ export class SuffixIndexes extends React.Component { ); }) .fail(err => { - // this.loadIndexes(); + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.closeIndexModal(); this.props.addNotification( "error", - `Error creating index - ${err}` + `Error creating index - ${errMsg.desc}` ); }); } @@ -328,9 +330,10 @@ export class SuffixIndexes extends React.Component { }); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.addNotification( "error", - `Error indexing attribute ${attr} - ${err}` + `Error indexing attribute ${attr} - ${errMsg.desc}` ); }); } @@ -421,11 +424,12 @@ export class SuffixIndexes extends React.Component { } }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.closeEditIndexModal(); this.props.addNotification( "error", - `Error editing index - ${err}` + `Error editing index - ${errMsg.desc}` ); }); } @@ -487,10 +491,11 @@ export class SuffixIndexes extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Error deleting index - ${err}` + `Error deleting index - ${errMsg.desc}` ); }); } diff --git a/src/cockpit/389-console/src/lib/database/referrals.jsx b/src/cockpit/389-console/src/lib/database/referrals.jsx index d86c8c4..22f9c50 100644 --- a/src/cockpit/389-console/src/lib/database/referrals.jsx +++ b/src/cockpit/389-console/src/lib/database/referrals.jsx @@ -90,10 +90,11 @@ export class SuffixReferrals extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Failure deleting referral - ${err}` + `Failure deleting referral - ${errMsg.desc}` ); }); } @@ -146,11 +147,12 @@ export class SuffixReferrals extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.closeRefModal(); this.props.addNotification( "error", - `Failure creating referral - ${err}` + `Failure creating referral - ${errMsg.desc}` ); }); } diff --git a/src/cockpit/389-console/src/lib/database/suffix.jsx b/src/cockpit/389-console/src/lib/database/suffix.jsx index aa3b99c..9cfb95b 100644 --- a/src/cockpit/389-console/src/lib/database/suffix.jsx +++ b/src/cockpit/389-console/src/lib/database/suffix.jsx @@ -196,9 +196,10 @@ export class Suffix extends React.Component { }); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.addNotification( "error", - `Error importing LDIF file - ${err}` + `Error importing LDIF file - ${errMsg.desc}` ); this.setState({ showImportModal: false @@ -278,10 +279,11 @@ export class Suffix extends React.Component { }); }) .fail(err => { + let errMsg = JSON.parse(err); this.loadLDIFs(); this.props.addNotification( "error", - `Error exporting database - ${err}` + `Error exporting database - ${errMsg.desc}` ); this.setState({ showExportModal: false, @@ -330,9 +332,10 @@ export class Suffix extends React.Component { }); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.addNotification( "error", - `Failed to reindex database - ${err}` + `Failed to reindex database - ${errMsg.desc}` ); this.setState({ showReindexModal: false, @@ -405,11 +408,12 @@ export class Suffix extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.loadSuffixTree(false); this.closeSubSuffixModal(); this.props.addNotification( "error", - `Error creating sub-suffix - ${err}` + `Error creating sub-suffix - ${errMsg.desc}` ); }); } @@ -527,11 +531,12 @@ export class Suffix extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.loadSuffixTree(false); this.closeLinkModal(); this.props.addNotification( "error", - `Error creating database link - ${err}` + `Error creating database link - ${errMsg.desc}` ); }); } @@ -594,11 +599,12 @@ export class Suffix extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.loadSuffixTree(true); this.closeLinkModal(); this.props.addNotification( "error", - `Error deleting database - ${err}` + `Error deleting database - ${errMsg.desc}` ); }); } @@ -655,10 +661,11 @@ export class Suffix extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Error updating suffix configuration - ${err}` + `Error updating suffix configuration - ${errMsg.desc}` ); }); } diff --git a/src/cockpit/389-console/src/lib/database/vlvIndexes.jsx b/src/cockpit/389-console/src/lib/database/vlvIndexes.jsx index 674f8ec..a4df2b9 100644 --- a/src/cockpit/389-console/src/lib/database/vlvIndexes.jsx +++ b/src/cockpit/389-console/src/lib/database/vlvIndexes.jsx @@ -224,11 +224,12 @@ export class VLVIndexes extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.closeVLVEditModal(); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Failed to edit VLV search - ${err}` + `Failed to edit VLV search - ${errMsg.desc}` ); }); } @@ -273,11 +274,12 @@ export class VLVIndexes extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.closeVLVEditModal(); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Failed to add VLV index entry - ${err}` + `Failed to add VLV index entry - ${errMsg.desc}` ); }); } @@ -304,11 +306,12 @@ export class VLVIndexes extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.closeVLVEditModal(); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Failed to add VLV index entry - ${err}` + `Failed to add VLV index entry - ${errMsg.desc}` ); }); } @@ -391,17 +394,19 @@ export class VLVIndexes extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.addNotification( "error", - `Failed create VLV index entry - ${err}` + `Failed create VLV index entry - ${errMsg.desc}` ); }); } }) .fail(err => { + let errMsg = JSON.parse(err); this.props.addNotification( "error", - `Failed create VLV search entry - ${err}` + `Failed create VLV search entry - ${errMsg.desc}` ); }); this.closeVLVModal(); @@ -455,10 +460,11 @@ export class VLVIndexes extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Failed to deletre VLV index - ${err}` + `Failed to deletre VLV index - ${errMsg.desc}` ); }); } @@ -492,10 +498,11 @@ export class VLVIndexes extends React.Component { ); }) .fail(err => { + let errMsg = JSON.parse(err); this.props.reload(this.props.suffix); this.props.addNotification( "error", - `Failed to index VLV index - ${err}` + `Failed to index VLV index - ${errMsg.desc}` ); }); } diff --git a/src/cockpit/389-console/src/lib/monitor/accesslog.jsx b/src/cockpit/389-console/src/lib/monitor/accesslog.jsx new file mode 100644 index 0000000..e373c08 --- /dev/null +++ b/src/cockpit/389-console/src/lib/monitor/accesslog.jsx @@ -0,0 +1,117 @@ +import React from "react"; +import PropTypes from "prop-types"; +import "../../css/ds.css"; +import { + Spinner, + noop, + Row, + Col, + ControlLabel, + Icon, +} from "patternfly-react"; + +export class AccessLogMonitor extends React.Component { + componentDidUpdate () { + // Set the textarea to be scrolled down to the bottom + let textarea = document.getElementById('accesslog-area'); + textarea.scrollTop = textarea.scrollHeight; + } + + render() { + let spinner = ""; + if (this.props.reloading) { + spinner = +
    + + Reloading access log... +
    ; + } + let contRefreshCheckbox = + ; + if (this.props.refreshing) { + contRefreshCheckbox = + ; + } + + let selectLines = +
    + +
    ; + + return ( +
    + + + + Access Log + this.props.reload(this.props.reload)} + /> + + + + {spinner} + + +

    + + + {selectLines} + + +

    + +
    + + + -

    -
    - - - - - - - - - - - - - - - - - - - - diff --git a/src/cockpit/389-console/src/monitor.js b/src/cockpit/389-console/src/monitor.js deleted file mode 100644 index 1774d15..0000000 --- a/src/cockpit/389-console/src/monitor.js +++ /dev/null @@ -1,398 +0,0 @@ - -var accesslog_cont_refresh; -var auditlog_cont_refresh; -var auditfaillog_cont_refresh; -var errorslog_cont_refresh; - -var sev_emerg = " - EMERG - "; -var sev_crit = " - CRIT - "; -var sev_alert = " - ALERT - "; -var sev_err = " - ERR - "; -var sev_warn = " - WARN - "; -var sev_notice = " - NOTICE - "; -var sev_info = " - INFO - "; -var sev_debug = " - DEBUG - "; -var sev_levels = {"Emergency": sev_emerg, - "Critical": sev_crit, - "Alert": sev_alert, - "Error": sev_err, - "Warning": sev_warn, - "Notice": sev_notice, - "Info": sev_info, - "Debug": sev_debug - }; -var sev_all_errs = [sev_emerg, sev_crit, sev_alert, sev_err]; -var sev_all_info = [sev_warn, sev_notice, sev_info, sev_debug]; - - -function gen_ratio_chart(ratio, chart ) { - var c3ChartDefaults = patternfly.c3ChartDefaults(); - var donutConfig = c3ChartDefaults.getDefaultDonutConfig(ratio + "%"); - var miss = 100 - ratio; - var donut_color = patternfly.pfPaletteColors.lightGreen; - if (ratio < 90) { - donut_color = patternfly.pfPaletteColors.red; - } - donutConfig.bindto = chart; - donutConfig.data = { - type: "donut", - columns: [ - ["Hit Ratio", ratio], - ["Miss", miss], - ], - colors: { - 'Hit Ratio': donut_color, - 'Miss': "#D8D8D8" - }, - order: null - }; - donutConfig.size = { - width: 120, - height: 80 - }; - - c3.generate(donutConfig); -}; - -function gen_util_chart(used, maxsize, hitratio, chart ) { - var c3ChartDefaults = patternfly.c3ChartDefaults(); - var ratio = Math.round((used / maxsize) * 100); - var donutConfig = c3ChartDefaults.getDefaultDonutConfig(ratio + "%"); - var avail = maxsize - used; - donutConfig.bindto = chart; - var donut_color = patternfly.pfPaletteColors.lightGreen; - if (hitratio < 90 && ratio > 90) { - donut_color = patternfly.pfPaletteColors.red; - } - - donutConfig.data = { - type: "donut", - columns: [ - ["Used", used], - ["Available", avail], - ], - colors: { - 'Used': donut_color, - 'Available': "#D8D8D8" - }, - order: null - }; - donutConfig.size = { - width: 120, - height: 80 - }; - c3.generate(donutConfig); -}; - -/* - * Refresh logs - */ -function refresh_access_log () { - var access_log = "/var/log/dirsrv/" + server_id + "/access"; // TODO - get actual log location from config - var lines = $("#accesslog-lines").val(); - var logging = cockpit.spawn(["tail", "-" + lines, access_log], - { "superuser": "try" }).done(function(data) { - $("#accesslog-area").text(data); - }); -} - -function refresh_audit_log () { - var audit_log = "/var/log/dirsrv/" + server_id + "/audit"; // TODO - get actual log location from config - var lines = $("#auditlog-lines").val(); - var logging = cockpit.spawn(["tail", "-" + lines, audit_log], - { "superuser": "try" }).done(function(data) { - $("#auditlog-area").text(data); - }); -} - -function refresh_auditfail_log () { - var auditfail_log = "/var/log/dirsrv/" + server_id + "/auditfail"; // TODO - get actual log location from config - var lines = $("#auditfaillog-lines").val(); - var logging = cockpit.spawn(["tail", "-" + lines, auditfail_log], - { "superuser": "try" }).done(function(data) { - $("#auditfaillog-area").text(data); - }); -} - -function refresh_errors_log () { - var errors_log = "/var/log/dirsrv/" + server_id + "/errors"; // TODO - get actual log location from config - var lines = $("#errorslog-lines").val(); - var sev_level = $("#errorslog-sev-level").val(); - var logging = cockpit.spawn(["tail", "-" + lines, errors_log], - { "superuser": "try" }).done(function(data) { - if (sev_level != "Everything"){ - // Filter Data - var lines = data.split('\n'); - var new_data = ""; - for (var i = 0; i < lines.length; i++){ - var line = ""; - if (sev_level == "Error Messages"){ - for (var lev = 0; lev < sev_all_errs.length; lev++) { - if (lines[i].indexOf(sev_all_errs[lev]) != -1){ - line = lines[i] + "\n"; - } - } - } else if (sev_level == "Info Messages"){ - for (var lev = 0; lev < sev_all_info.length; lev++) { - if (lines[i].indexOf(sev_all_info[lev]) != -1){ - line = lines[i] + "\n"; - } - } - } else if (lines[i].indexOf(sev_levels[sev_level]) != -1){ - line = lines[i] + "\n"; - } - // Add the filtered line to new data - new_data += line; - } - data = new_data; - } - $("#errorslog-area").text(data); - }); -} - - -$(document).ready( function() { - $("#monitor-content").load("monitor.html", function () { - $('#monitor-db-tree').jstree({ - "plugins": [ "contextmenu", "wholerow" ], - }); - - $("#monitor-server-btn").on("click", function() { - $(".all-pages").hide(); - $("#monitor-content").show(); - $("#monitor-server-page").show(); - }); - - $("#monitor-db-btn").on("click", function() { - $(".all-pages").hide(); - $("#monitor-content").show(); - - // TODO - NDN cache prior to 1.4.0 is duplicated under each suffix/backend monitor - - // so we need to bring it forward to the global database stats here - var db_hitratio = '99'; - var ndn_hitratio = '83'; - var ndn_maxsize = '25165824'; - var ndn_cursize = '19891200'; - gen_ratio_chart(db_hitratio, '#monitor-db-cache-hitratio-chart'); - gen_ratio_chart(ndn_hitratio, '#monitor-ndn-cache-hitratio-chart'); - gen_util_chart(ndn_cursize, ndn_maxsize, ndn_hitratio, '#monitor-ndn-cache-util-chart'); - - $("#monitor-db-page").show(); - }); - - - $("#monitor-snmp-btn").on("click", function() { - $(".all-pages").hide(); - $("#monitor-content").show(); - $("#monitor-snmp-page").show(); - }); - - $("#monitor-repl-btn").on("click", function() { - $(".all-pages").hide(); - $("#monitor-content").show(); - $("#monitor-repl-page").show(); - }); - - $("#monitor-log-access-btn").on("click", function() { - $(".all-pages").hide(); - $("#monitor-content").show(); - refresh_access_log(); - $("#monitor-log-access-page").show(); - }); - $("#monitor-log-audit-btn").on("click", function() { - $(".all-pages").hide(); - $("#monitor-content").show(); - refresh_audit_log(); - $("#monitor-log-audit-page").show(); - }); - $("#monitor-log-auditfail-btn").on("click", function() { - $(".all-pages").hide(); - $("#monitor-content").show(); - refresh_auditfail_log() - $("#monitor-log-auditfail-page").show(); - }); - $("#monitor-log-errors-btn").on("click", function() { - $(".all-pages").hide(); - $("#monitor-content").show(); - refresh_errors_log(); - $("#monitor-log-errors-page").show(); - }); - - $("#accesslog-refresh-btn").on('click', function () { - refresh_access_log(); - }); - $("#auditlog-refresh-btn").on('click', function () { - refresh_audit_log(); - }); - $("#auditfaillog-refresh-btn").on('click', function () { - refresh_auditfail_log(); - }); - $("#errorslog-refresh-btn").on('click', function () { - refresh_errors_log(); - }); - - $('#monitor-db-tree').on("changed.jstree", function (e, data) { - var tree_node = data.selected; - if (tree_node == "monitor-db-main") { - - // TODO - NDN cache prior to 1.4.0 is duplicated under each suffix/backend monitor - - // so we need to bring it forward to the global database stats here - var db_hitratio = '99'; - var ndn_hitratio = '83'; - var ndn_maxsize = '25165824'; - var ndn_cursize = '19891200'; - - gen_ratio_chart(db_hitratio, '#monitor-db-cache-hitratio-chart'); - gen_ratio_chart(ndn_hitratio, '#monitor-ndn-cache-hitratio-chart'); - gen_util_chart(ndn_cursize, ndn_maxsize, ndn_hitratio, '#monitor-ndn-cache-util-chart'); - $("#monitor-suffix-page").hide(); - $("#db-content").show(); - } else if (tree_node[0].startsWith("monitor-suffix-")) { - /* - * Gather and set the Suffix info - */ - var monitor_suffix = tree_node[0].replace("monitor-suffix-", ""); - $("#monitor-suffix-header").html("" + monitor_suffix + ""); - - // TODO - get the monitor info. For now uses DEMO values for the charts - var entry_hitratio = '96'; - var entry_maxsize = '512000'; - var entry_cursize = '395000'; - var dn_hitratio = '89'; - var dn_maxsize = '51200'; - var dn_cursize = '51200'; - - // Generate the donut charts - gen_ratio_chart(entry_hitratio, '#monitor-entry-cache-hitratio-chart'); - gen_util_chart(entry_cursize, entry_maxsize, entry_hitratio, '#monitor-entry-cache-util-chart'); - gen_ratio_chart(dn_hitratio, '#monitor-dn-cache-hitratio-chart'); - gen_util_chart(dn_cursize, dn_maxsize, dn_hitratio, '#monitor-dn-cache-util-chart'); - $("#db-content").hide(); - $("#monitor-suffix-page").show(); - } - }); - - var monitor_conn_table = $('#monitor-conn-table').DataTable( { - "paging": true, - "bAutoWidth": false, - "searching": false, - "dom": '<"pull-left"f><"pull-right"l>tip', - "lengthChange": false, - "lengthMenu": [ 10, 25, 50, 100], - "language": { - "emptyTable": "No active connections", - "search": "Search Connections" - } - }); - var monitor_index_table = $('#monitor-index-table').DataTable( { - "paging": true, - "bAutoWidth": false, - "dom": '<"pull-left"f><"pull-right"l>tip', - "lengthMenu": [ 10, 25, 50, 100], - "language": { - "emptyTable": "No Attribute Indexes", - "search": "Search Indexes" - } - }); - - $.fn.dataTable.moment( 'HH:mm:ss' ); - var monitor_repl_table = $('#monitor-repl-table').DataTable( { - "paging": true, - "bAutoWidth": false, - "dom": '<"pull-left"f><"pull-right"l>tip', - "lengthMenu": [ 10, 25, 50, 100], - "language": { - "emptyTable": "No Replication Agreements", - "search": "Search" - } - }); - - var monitor_winsync_table = $('#monitor-winsync-table').DataTable( { - "paging": true, - "bAutoWidth": false, - "dom": '<"pull-left"f><"pull-right"l>tip', - "lengthMenu": [ 10, 25, 50, 100], - "language": { - "emptyTable": "No Winsync Agreements", - "search": "Search" - } - }); - - // The continuous log refresh intervals - $("#accesslog-cont-refresh").change(function() { - if(this.checked) { - accesslog_cont_refresh = setInterval(refresh_access_log, 1000); - } else { - clearInterval(accesslog_cont_refresh); - } - }); - - $("#auditlog-cont-refresh").change(function() { - if(this.checked) { - auditlog_cont_refresh = setInterval(refresh_audit_log, 1000); - } else { - clearInterval(auditlog_cont_refresh); - } - }); - - $("#auditfaillog-cont-refresh").change(function() { - if(this.checked) { - auditfaillog_cont_refresh = setInterval(refresh_auditfail_log, 1000); - } else { - clearInterval(auditfaillog_cont_refresh); - } - }); - - $("#errorslog-cont-refresh").change(function() { - if(this.checked) { - errorslog_cont_refresh = setInterval(refresh_errors_log, 1000); - } else { - clearInterval(errorslog_cont_refresh); - } - }); - - // Refresh page after changing severity level - $("#errorslog-sev-level").on("change", function() { - refresh_errors_log(); - }); - - $(document).on('click', '.repl-detail-btn', function(e) { - e.preventDefault(); - var data = monitor_repl_table.row( $(this).parents('tr') ).data(); - var agmt_name = data[0]; - var agmt_suffix = data[2]; - var agmt_enabled = "off"; // TODO Need to determine this from DS data - var agmt_status = ""; - if (agmt_enabled == "off") { - agmt_status = " (Agreement Disabled)"; - } - // clear_agmt_form(); - - $("#monitor-agmt-header").html("Replication Agreement Details:   " + agmt_name + " " + agmt_status); - - // TODO - get agreement details and populate form - $("#monitor-agmt-form").css('display', 'block'); - }); - - - $(document).on('click', '.repl-winsync-detail-btn', function(e) { - e.preventDefault(); - var data = monitor_repl_table.row( $(this).parents('tr') ).data(); - var agmt_name = data[0]; - var agmt_suffix = data[2]; - var agmt_enabled = "on"; // TODO Need to determine this from DS data - var agmt_status = ""; - if (agmt_enabled == "off") { - agmt_status = " (Agreement Disabled)"; - } - // clear_agmt_form(); - - $("#repl-winsync-agmt-header").html("Winsync Agreement Details:   " + agmt_name + " " + agmt_status); - // TODO - get agreement details and populate form - $("#monitor-winsync-agmt-form").css('display', 'block'); - }); - - // Page is loaded, mark it as so... - monitor_page_loaded = 1; - }); -}); diff --git a/src/cockpit/389-console/src/monitor.jsx b/src/cockpit/389-console/src/monitor.jsx new file mode 100644 index 0000000..ae67f4a --- /dev/null +++ b/src/cockpit/389-console/src/monitor.jsx @@ -0,0 +1,1187 @@ +import cockpit from "cockpit"; +import React from "react"; +import { NotificationController } from "./lib/notifications.jsx"; +import { log_cmd } from "./lib/tools.jsx"; +import { + TreeView, + Spinner, + Row, + Col, + Icon, + ControlLabel +} from "patternfly-react"; +import PropTypes from "prop-types"; +import SNMPMonitor from "./lib/monitor/snmpMonitor.jsx"; +import ServerMonitor from "./lib/monitor/serverMonitor.jsx"; +import DatabaseMonitor from "./lib/monitor/dbMonitor.jsx"; +import SuffixMonitor from "./lib/monitor/suffixMonitor.jsx"; +import ChainingMonitor from "./lib/monitor/chainingMonitor.jsx"; +import AccessLogMonitor from "./lib/monitor/accesslog.jsx"; +import AuditLogMonitor from "./lib/monitor/auditlog.jsx"; +import AuditFailLogMonitor from "./lib/monitor/auditfaillog.jsx"; +import ErrorLogMonitor from "./lib/monitor/errorlog.jsx"; +import ReplMonitor from "./lib/monitor/replMonitor.jsx"; +import "./css/ds.css"; + +const treeViewContainerStyles = { + width: '295px', +}; + +export class Monitor extends React.Component { + constructor(props) { + super(props); + this.state = { + nodes: [], + node_name: "", + node_text: "", + node_type: "", + loaded: false, + snmpData: {}, + ldbmData: {}, + serverData: {}, + showLoading: false, + loadingMsg: "", + notifications: [], + // Suffix + suffixLoading: false, + serverLoading: false, + ldbmLoading: false, + snmpLoading: false, + chainingLoading: false, + // replication + replLoading: false, + replInitLoaded: false, + replSuffix: "", + replRole: "", + replRid: "", + replicatedSuffixes: [], + // Access log + accesslogLocation: "", + accesslogData: "", + accessReloading: false, + accesslog_cont_refresh: "", + accessRefreshing: false, + accessLines: "50", + // Audit log + auditlogLocation: "", + auditlogData: "", + auditReloading: false, + auditlog_cont_refresh: "", + auditRefreshing: false, + auditLines: "50", + // Audit Fail log + auditfaillogLocation: "", + auditfaillogData: "", + auditfailReloading: false, + auditfaillog_cont_refresh: "", + auditfailRefreshing: false, + auditfailLines: "50", + // Error log + errorlogLocation: "", + errorlogData: "", + errorReloading: false, + errorlog_cont_refresh: "", + errorRefreshing: false, + errorSevLevel: "Everything", + errorLines: "50", + }; + + // Build the log severity sev_levels + let sev_emerg = " - EMERG - "; + let sev_crit = " - CRIT - "; + let sev_alert = " - ALERT - "; + let sev_err = " - ERR - "; + let sev_warn = " - WARN - "; + let sev_notice = " - NOTICE - "; + let sev_info = " - INFO - "; + let sev_debug = " - DEBUG - "; + this.sev_levels = { + "Emergency": sev_emerg, + "Critical": sev_crit, + "Alert": sev_alert, + "Error": sev_err, + "Warning": sev_warn, + "Notice": sev_notice, + "Info": sev_info, + "Debug": sev_debug + }; + this.sev_all_errs = [sev_emerg, sev_crit, sev_alert, sev_err]; + this.sev_all_info = [sev_warn, sev_notice, sev_info, sev_debug]; + + // Bindings + this.addNotification = this.addNotification.bind(this); + this.removeNotification = this.removeNotification.bind(this); + this.loadSuffixTree = this.loadSuffixTree.bind(this); + this.update_tree_nodes = this.update_tree_nodes.bind(this); + this.selectNode = this.selectNode.bind(this); + this.loadMonitorSuffix = this.loadMonitorSuffix.bind(this); + this.loadMonitorLDBM = this.loadMonitorLDBM.bind(this); + this.reloadLDBM = this.reloadLDBM.bind(this); + this.loadMonitorSNMP = this.loadMonitorSNMP.bind(this); + this.reloadSNMP = this.reloadSNMP.bind(this); + this.loadMonitorServer = this.loadMonitorServer.bind(this); + this.reloadServer = this.reloadServer.bind(this); + this.loadMonitorChaining = this.loadMonitorChaining.bind(this); + // Replication + this.loadMonitorReplication = this.loadMonitorReplication.bind(this); + this.loadCleanTasks = this.loadCleanTasks.bind(this); + this.loadAbortTasks = this.loadAbortTasks.bind(this); + this.loadReplicatedSuffixes = this.loadReplicatedSuffixes.bind(this); + this.loadWinsyncAgmts = this.loadWinsyncAgmts.bind(this); + this.replSuffixChange = this.replSuffixChange.bind(this); + this.reloadReplAgmts = this.reloadReplAgmts.bind(this); + this.reloadReplWinsyncAgmts = this.reloadReplWinsyncAgmts.bind(this); + // Logging + this.loadMonitor = this.loadMonitor.bind(this); + this.refreshAccessLog = this.refreshAccessLog.bind(this); + this.refreshAuditLog = this.refreshAuditLog.bind(this); + this.refreshAuditFailLog = this.refreshAuditFailLog.bind(this); + this.refreshErrorLog = this.refreshErrorLog.bind(this); + this.handleAccessChange = this.handleAccessChange.bind(this); + this.handleAuditChange = this.handleAuditChange.bind(this); + this.handleAuditFailChange = this.handleAuditFailChange.bind(this); + this.handleErrorChange = this.handleErrorChange.bind(this); + this.accessRefreshCont = this.accessRefreshCont.bind(this); + this.auditRefreshCont = this.auditRefreshCont.bind(this); + this.auditFailRefreshCont = this.auditFailRefreshCont.bind(this); + this.errorRefreshCont = this.errorRefreshCont.bind(this); + this.handleSevChange = this.handleSevChange.bind(this); + } + + componentDidMount() { + this.loadMonitor(); + } + + componentDidUpdate(prevProps) { + if (this.props.serverId !== prevProps.serverId) { + this.loadSuffixTree(false); + } + } + + addNotification(type, message, timerdelay, persistent) { + this.setState(prevState => ({ + notifications: [ + ...prevState.notifications, + { + key: prevState.notifications.length + 1, + type: type, + persistent: persistent, + timerdelay: timerdelay, + message: message, + } + ] + })); + } + + removeNotification(notificationToRemove) { + this.setState({ + notifications: this.state.notifications.filter( + notification => notificationToRemove.key !== notification.key + ) + }); + } + + loadSuffixTree(fullReset) { + const cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "backend", "get-tree", + ]; + log_cmd("getTree", "Start building the suffix tree", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let treeData = JSON.parse(content); + let basicData = [ + { + text: "Database", + selectable: true, + selected: true, + icon: "fa fa-database", + state: {"expanded": true}, + id: "database-monitor", + type: "database", + nodes: [] + }, + { + text: "Logging", + icon: "pficon-catalog", + selectable: false, + id: "log-monitor", + nodes: [ + { + text: "Access Log", + icon: "glyphicon glyphicon-book", + selectable: true, + id: "access-log-monitor", + type: "log", + }, + { + text: "Audit Log", + icon: "glyphicon glyphicon-book", + selectable: true, + id: "audit-log-monitor", + type: "log", + }, + { + text: "Audit Failure Log", + icon: "glyphicon glyphicon-book", + selectable: true, + id: "auditfail-log-monitor", + type: "log", + }, + { + text: "Errors Log", + icon: "glyphicon glyphicon-book", + selectable: true, + id: "error-log-monitor", + type: "log", + }, + ] + }, + { + text: "Replication", + selectable: true, + icon: "pficon-topology", + id: "replication-monitor", + type: "replication", + }, + { + text: "Server Statistics", + icon: "pficon-server", + selectable: true, + id: "server-monitor", + type: "server", + }, + { + text: "SNMP Counters", + icon: "glyphicon glyphicon-list-alt", + selectable: true, + id: "snmp-monitor", + type: "snmp", + }, + + ]; + let current_node = this.state.node_name; + let type = this.state.node_type; + if (fullReset) { + current_node = "database-monitor"; + type = "database"; + } + basicData[0].nodes = treeData; + this.setState(() => ({ + nodes: basicData, + node_name: current_node, + node_type: type, + }), this.update_tree_nodes); + }); + } + + selectNode(selectedNode) { + this.setState({ + showLoading: true + }); + + if (selectedNode.id == "database-monitor" || + selectedNode.id == "server-monitor" || + selectedNode.id == "snmp-monitor") { + // Nothing special to do, these configurations have already been loaded + this.setState(prevState => { + return { + nodes: this.nodeSelector(prevState.nodes, selectedNode), + node_name: selectedNode.id, + node_text: selectedNode.text, + bename: "", + }; + }); + } else if (selectedNode.id == "access-log-monitor") { + this.refreshAccessLog(); + this.setState(prevState => { + return { + nodes: this.nodeSelector(prevState.nodes, selectedNode), + node_name: selectedNode.id, + node_text: selectedNode.text, + }; + }); + } else if (selectedNode.id == "audit-log-monitor") { + this.refreshAuditLog(); + this.setState(prevState => { + return { + nodes: this.nodeSelector(prevState.nodes, selectedNode), + node_name: selectedNode.id, + node_text: selectedNode.text, + }; + }); + } else if (selectedNode.id == "auditfail-log-monitor") { + this.refreshAuditFailLog(); + this.setState(prevState => { + return { + nodes: this.nodeSelector(prevState.nodes, selectedNode), + node_name: selectedNode.id, + node_text: selectedNode.text, + }; + }); + } else if (selectedNode.id == "error-log-monitor") { + this.refreshErrorLog(); + this.setState(prevState => { + return { + nodes: this.nodeSelector(prevState.nodes, selectedNode), + node_name: selectedNode.id, + node_text: selectedNode.text, + }; + }); + } else if (selectedNode.id == "replication-monitor") { + if (!this.state.replInitLoaded) { + this.loadMonitorReplication(); + } + this.setState(prevState => { + return { + nodes: this.nodeSelector(prevState.nodes, selectedNode), + node_name: selectedNode.id, + node_text: selectedNode.text, + }; + }); + } else { + if (selectedNode.id in this.state) { + // This suffix is already cached, but it might be incomplete... + if (selectedNode.type == "dblink" && this.state.nsaddcount === undefined) { + this.loadMonitorChaining(selectedNode.id); + } else if (selectedNode.type != "dblink" && this.state.entrycachehitratio === undefined) { + this.loadMonitorSuffix(selectedNode.id); + } + this.setState(prevState => { + return { + nodes: this.nodeSelector(prevState.nodes, selectedNode), + node_name: selectedNode.id, + node_text: selectedNode.text, + node_type: selectedNode.type, + bename: selectedNode.be, + }; + }); + } else { + // Load this suffix (db, chaining & replication) + if (selectedNode.type == "dblink") { + // Chaining + this.loadMonitorChaining(selectedNode.id); + } else { + // Suffix + this.loadMonitorSuffix(selectedNode.id); + } + this.setState(prevState => { + return { + nodes: this.nodeSelector(prevState.nodes, selectedNode), + node_name: selectedNode.id, + node_text: selectedNode.text, + node_type: selectedNode.type, + bename: selectedNode.be, + }; + }); + } + } + } + + nodeSelector(nodes, targetNode) { + return nodes.map(node => { + if (node.nodes) { + return { + ...node, + nodes: this.nodeSelector(node.nodes, targetNode), + selected: node.id === targetNode.id ? !node.selected : false + }; + } else if (node.id === targetNode.id) { + return { ...node, selected: !node.selected }; + } else if (node.id !== targetNode.id && node.selected) { + return { ...node, selected: false }; + } else { + return node; + } + }); + } + + update_tree_nodes() { + // Set title to the text value of each suffix node. We need to do this + // so we can read long suffixes in the UI tree div + let elements = document.getElementsByClassName('treeitem-row'); + for (let el of elements) { + el.setAttribute('title', el.innerText); + } + this.setState({ + loaded: true + }); + } + + loadMonitor() { + // Load the following componets in a chained fashion: + // - log file locations + // - LDBM + // - Server stats + // - SNMP + // - Finally load the "tree" + // + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "config", "get", "nsslapd-auditlog", "nsslapd-accesslog", "nsslapd-errorlog", "nsslapd-auditfaillog" + ]; + log_cmd("loadLogLocations", "Get log locations", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + accesslogLocation: config.attrs['nsslapd-accesslog'][0], + auditlogLocation: config.attrs['nsslapd-auditlog'][0], + auditfaillogLocation: config.attrs['nsslapd-auditfaillog'][0], + errorlogLocation: config.attrs['nsslapd-errorlog'][0], + }); + }, this.loadReplicatedSuffixes()); + } + + loadReplicatedSuffixes() { + // Load replicated suffix to populate the dropdown select list + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "replication", "list" + ]; + log_cmd("loadReplicatedSuffixes", "Load replication suffixes", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + let replSuffix = ""; + if (config.items.length > 0) { + replSuffix = config.items[0]; + } + this.setState({ + replicatedSuffixes: config.items, + replSuffix: replSuffix, + }); + }, this.loadMonitorLDBM()); + } + + loadMonitorLDBM() { + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "monitor", "ldbm" + ]; + log_cmd("loadMonitorLDBM", "Load database monitor", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + ldbmData: config.attrs + }); + }, this.loadMonitorServer()); + } + + reloadLDBM() { + this.setState({ + ldbmLoading: true + }); + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "monitor", "ldbm" + ]; + log_cmd("reloadLDBM", "Load database monitor", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + ldbmLoading: false, + ldbmData: config.attrs + }); + }); + } + + loadMonitorServer() { + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "monitor", "server" + ]; + log_cmd("loadMonitorServer", "Load server monitor", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + serverData: config.attrs + }); + }, this.loadMonitorSNMP()); + } + + reloadServer() { + this.setState({ + serverLoading: true + }); + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "monitor", "server" + ]; + log_cmd("reloadServer", "Load server monitor", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + serverLoading: false, + serverData: config.attrs + }); + }); + } + + loadMonitorSNMP() { + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "monitor", "snmp" + ]; + log_cmd("loadMonitorSNMP", "Load snmp monitor", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + snmpData: config.attrs, + }, this.loadSuffixTree(true)); + }); + } + + reloadSNMP() { + this.setState({ + snmpLoading: true + }); + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "monitor", "snmp" + ]; + log_cmd("reloadSNMP", "Load snmp monitor", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + snmpLoading: false, + snmpData: config.attrs, + }); + }); + } + + loadMonitorChaining(suffix) { + this.setState({ + chainingLoading: true + }); + + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "monitor", "chaining", suffix + ]; + log_cmd("loadMonitorChaining", "Load suffix monitor", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + [suffix]: { + ...this.state[suffix], + chainingData: config.attrs, + }, + chainingLoading: false, + }); + }) + .fail(() => { + // Notification of failure (could only be server down) + this.setState({ + chainingLoading: false, + }); + }); + } + + loadMonitorSuffix(suffix) { + this.setState({ + suffixLoading: true + }); + + let cmd = [ + "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "monitor", "backend", suffix + ]; + log_cmd("loadMonitorSuffix", "Load suffix monitor", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + [suffix]: { + ...this.state[suffix], + suffixData: config.attrs, + }, + suffixLoading: false, + }); + }) + .fail(() => { + // Notification of failure (could only be server down) + this.setState({ + suffixLoading: false, + }); + }); + } + + loadCleanTasks() { + let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "repl-tasks", "list-cleanruv-tasks", "--suffix=" + this.state.replSuffix]; + log_cmd("loadCleanTasks", "Load clean tasks", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + [this.state.replSuffix]: { + ...this.state[this.state.replSuffix], + cleanTasks: config.items, + }, + }, this.loadAbortTasks()); + }) + .fail(() => { + // Notification of failure (could only be server down) + this.setState({ + replLoading: false, + }); + }); + } + + loadAbortTasks() { + let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "repl-tasks", "list-abortruv-tasks", "--suffix=" + this.state.replSuffix]; + log_cmd("loadAbortCleanTasks", "Load abort tasks", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + [this.state.replSuffix]: { + ...this.state[this.state.replSuffix], + abortTasks: config.items, + }, + }, this.setState( + { + replLoading: false, + replInitLoaded: true + })); + }) + .fail(() => { + // Notification of failure (could only be server down) + this.setState({ + replLoading: false, + }); + }); + } + + loadWinsyncAgmts() { + let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "replication", "winsync-status", "--suffix=" + this.state.replSuffix]; + log_cmd("loadWinsyncAgmts", "Load winsync agmt status", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + [this.state.replSuffix]: { + ...this.state[this.state.replSuffix], + replWinsyncAgmts: config.items, + }, + }, this.loadCleanTasks()); + }) + .fail(() => { + // Notification of failure (could only be server down) + this.setState({ + replLoading: false, + }); + }); + } + + loadMonitorReplication() { + let replSuffix = this.state.replSuffix; + if (replSuffix != "") { + this.setState({ + replLoading: true + }); + + // Now load the agmts + let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "replication", "status", "--suffix=" + replSuffix]; + log_cmd("loadMonitorReplication", "Load replication agmts", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + [replSuffix]: { + ...this.state[replSuffix], + replAgmts: config.items, + abortTasks: [], + cleanTasks: [], + replWinsyncAgmts: [], + }, + }, this.loadWinsyncAgmts()); + }) + .fail(() => { + // Notification of failure (could only be server down) + this.setState({ + replLoading: false, + }); + }); + } + } + + reloadReplAgmts() { + let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "replication", "status", "--suffix=" + this.state.replSuffix]; + log_cmd("reloadReplAgmts", "Load replication agmts", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + [this.state.replSuffix]: { + ...this.state[this.state.replSuffix], + replAgmts: config.items, + }, + }); + }); + } + + reloadReplWinsyncAgmts() { + let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "replication", "winsync-status", "--suffix=" + this.state.replSuffix]; + log_cmd("reloadReplWinsyncAgmts", "Load winysnc agmts", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + let config = JSON.parse(content); + this.setState({ + [this.state.replSuffix]: { + ...this.state[this.state.replSuffix], + replWinsyncAgmts: config.items, + }, + }); + }); + } + + refreshAccessLog () { + this.setState({ + accessReloading: true + }); + let cmd = ["tail", "-" + this.state.accessLines, this.state.accesslogLocation]; + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + this.setState(() => ({ + accesslogData: content, + accessReloading: false + })); + }); + } + + refreshAuditLog () { + this.setState({ + auditReloading: true + }); + let cmd = ["tail", "-" + this.state.auditLines, this.state.auditlogLocation]; + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + this.setState(() => ({ + auditlogData: content, + auditReloading: false + })); + }); + } + + refreshAuditFailLog () { + this.setState({ + auditfailReloading: true + }); + let cmd = ["tail", "-" + this.state.auditfailLines, this.state.auditfaillogLocation]; + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + this.setState(() => ({ + auditfaillogData: content, + auditfailReloading: false + })); + }); + } + + refreshErrorLog () { + this.setState({ + errorReloading: true + }); + + let cmd = ["tail", "-" + this.state.errorLines, this.state.errorlogLocation]; + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(data => { + if (this.state.errorSevLevel != "Everything") { + // Filter Data + let lines = data.split('\n'); + let new_data = ""; + for (let i = 0; i < lines.length; i++) { + let line = ""; + if (this.state.errorSevLevel == "Error Messages") { + for (let lev of this.sev_all_errs) { + if (lines[i].indexOf(lev) != -1) { + line = lines[i] + "\n"; + } + } + } else if (this.state.errorSevLevel == "Info Messages") { + for (let lev of this.sev_all_info) { + if (lines[i].indexOf(lev) != -1) { + line = lines[i] + "\n"; + } + } + } else if (lines[i].indexOf(this.sev_levels[this.state.errorSevLevel]) != -1) { + line = lines[i] + "\n"; + } + // Add the filtered line to new data + new_data += line; + } + data = new_data; + } + + this.setState(() => ({ + errorlogData: data, + errorReloading: false + })); + }); + } + + accessRefreshCont(e) { + if (e.target.checked) { + this.state.accesslog_cont_refresh = setInterval(this.refreshAccessLog, 2000); + } else { + clearInterval(this.state.accesslog_cont_refresh); + } + this.setState({ + accessRefreshing: e.target.checked, + }); + } + + auditRefreshCont(e) { + if (e.target.checked) { + this.state.auditlog_cont_refresh = setInterval(this.refreshAuditLog, 2000); + } else { + clearInterval(this.state.auditlog_cont_refresh); + } + this.setState({ + auditRefreshing: e.target.checked, + }); + } + + auditFailRefreshCont(e) { + if (e.target.checked) { + this.state.auditfaillog_cont_refresh = setInterval(this.refreshAuditFailLog, 2000); + } else { + clearInterval(this.state.auditfaillog_cont_refresh); + } + this.setState({ + auditfailRefreshing: e.target.checked, + }); + } + + errorRefreshCont(e) { + if (e.target.checked) { + this.state.errorlog_cont_refresh = setInterval(this.refreshErrorLog, 2000); + } else { + clearInterval(this.state.errorlog_cont_refresh); + } + this.setState({ + errorRefreshing: e.target.checked, + }); + } + + handleAccessChange(e) { + let value = e.target.value; + this.setState(() => ( + { + accessLines: value + } + ), this.refreshAccessLog); + } + + handleAuditChange(e) { + let value = e.target.value; + this.setState(() => ( + { + auditLines: value + } + ), this.refreshAuditLog); + } + + handleAuditFailChange(e) { + let value = e.target.value; + this.setState(() => ( + { + auditfailLines: value + } + ), this.refreshAuditFailLog); + } + + handleErrorChange(e) { + let value = e.target.value; + this.setState(() => ( + { + errorLines: value + } + ), this.refreshErrorLog); + } + + handleSevChange(e) { + const value = e.target.value; + + this.setState({ + errorSevLevel: value, + }, this.refreshErrorLog); + } + + replSuffixChange(e) { + let value = e.target.value; + this.setState(() => ( + { + replSuffix: value, + replLoading: true + } + ), this.loadMonitorReplication); + } + + render() { + const { nodes } = this.state; + let monitorPage = ""; + let monitor_element = ""; + + if (this.state.loaded) { + if (this.state.node_name == "database-monitor" || this.state.node_name == "") { + if (this.state.ldbmLoading) { + monitor_element = +
    +

    +

    Loading database monitor information ...

    + +
    ; + } else { + monitor_element = + ; + } + } else if (this.state.node_name == "server-monitor") { + if (this.state.serverLoading) { + monitor_element = +
    +

    +

    Loading server monitor information ...

    + +
    ; + } else { + monitor_element = + ; + } + } else if (this.state.node_name == "snmp-monitor") { + if (this.state.snmpLoading) { + monitor_element = +
    +

    +

    Loading SNMP monitor information ...

    + +
    ; + } else { + monitor_element = + ; + } + } else if (this.state.node_name == "access-log-monitor") { + monitor_element = + ; + } else if (this.state.node_name == "audit-log-monitor") { + monitor_element = + ; + } else if (this.state.node_name == "auditfail-log-monitor") { + monitor_element = + ; + } else if (this.state.node_name == "error-log-monitor") { + monitor_element = + ; + } else if (this.state.node_name == "replication-monitor") { + if (this.state.replLoading) { + monitor_element = +
    +

    +

    Loading replication monitor information ...

    + +
    ; + } else { + if (this.state.replicatedSuffixes.length < 1) { + monitor_element = +
    +

    There are no suffixes that have been configured for replication

    +
    ; + } else { + let suffixList = this.state.replicatedSuffixes.map((suffix) => + + ); + monitor_element = +
    + + + Replication Monitoring + + this.loadMonitorReplication()} + /> + + +
    + +
    +
    ; + } + } + } else if (this.state.node_name != "") { + // suffixes (example) + if (this.state.suffixLoading) { + monitor_element = +
    +

    +

    Loading suffix monitor information for {this.state.node_text} ...

    + +
    ; + } else if (this.state.chainingLoading) { + monitor_element = +
    +

    +

    Loading chaining monitor information for {this.state.node_text} ...

    + +
    ; + } else { + if (this.state.node_type == "dblink") { + monitor_element = + ; + } else { + // Suffix + monitor_element = + ; + } + } + } + monitorPage = +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + {monitor_element} +
    +
    +
    ; + } else { + monitorPage = +
    +

    +

    Loading monitor information ...

    + +
    ; + } + + return ( +
    + {monitorPage} +
    + ); + } +} + +// Property types and defaults + +Monitor.propTypes = { + serverId: PropTypes.string +}; + +Monitor.defaultProps = { + serverId: "" +}; diff --git a/src/cockpit/389-console/src/plugins.jsx b/src/cockpit/389-console/src/plugins.jsx index cab8952..ac9f8b7 100644 --- a/src/cockpit/389-console/src/plugins.jsx +++ b/src/cockpit/389-console/src/plugins.jsx @@ -182,7 +182,8 @@ export class Plugins extends React.Component { }) .fail(err => { if (err != 0) { - console.log("pluginList failed", err); + let errMsg = JSON.parse(err); + console.log("pluginList failed: ", errMsg.desc); } this.toggleLoading(); }); @@ -238,12 +239,13 @@ export class Plugins extends React.Component { this.toggleLoading(); }) .fail(err => { - if (err.message.indexOf("nothing to set") >= 0) { + let errMsg = JSON.parse(err); + if (errMsg.desc.indexOf("nothing to set") >= 0) { nothingToSetErr = true; } else { this.addNotification( "error", - `${err.message} error during ${data.name} modification` + `${errMsg.desc} error during ${data.name} modification` ); } this.closePluginModal(); @@ -275,12 +277,13 @@ export class Plugins extends React.Component { console.info("savePlugin", "Result", content); }) .fail(err => { + let errMsg = JSON.parse(err); if ( - (err.message.indexOf( + (errMsg.desc.indexOf( "nothing to set" ) >= 0 && - nothingToSetErr) || - err.message.indexOf("nothing to set") < 0 + nothingToSetErr) || + errMsg.desc.indexOf("nothing to set") < 0 ) { if (basicPluginSuccess) { this.addNotification( @@ -291,7 +294,7 @@ export class Plugins extends React.Component { } this.addNotification( "error", - `${err.message} error during ${data.name} modification` + `${errMsg.desc} error during ${data.name} modification` ); } this.toggleLoading(); diff --git a/src/cockpit/389-console/src/replication.html b/src/cockpit/389-console/src/replication.html index 06a26e8..f68c837 100644 --- a/src/cockpit/389-console/src/replication.html +++ b/src/cockpit/389-console/src/replication.html @@ -181,8 +181,6 @@ Winsync Agreements