This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.0 by this push:
new 1d13ff2 Ticket 50291 - Add monitor tab functionality to Cockpit UI
1d13ff2 is described below
commit 1d13ff252cd1f9b672bd6399dd556c57bb4eace7
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Tue Apr 9 23:01:33 2019 -0400
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)
---
src/cockpit/389-console/src/css/ds.css | 98 +-
src/cockpit/389-console/src/database.jsx | 23 +-
src/cockpit/389-console/src/ds.js | 52 +-
src/cockpit/389-console/src/index.es6 | 13 +-
src/cockpit/389-console/src/index.html | 34 +-
.../src/lib/database/attrEncryption.jsx | 6 +-
.../389-console/src/lib/database/backups.jsx | 18 +-
.../389-console/src/lib/database/chaining.jsx | 25 +-
.../src/lib/database/databaseConfig.jsx | 5 +-
.../389-console/src/lib/database/indexes.jsx | 19 +-
.../389-console/src/lib/database/referrals.jsx | 6 +-
.../389-console/src/lib/database/suffix.jsx | 21 +-
.../389-console/src/lib/database/vlvIndexes.jsx | 21 +-
.../389-console/src/lib/monitor/accesslog.jsx | 117 ++
.../389-console/src/lib/monitor/auditfaillog.jsx | 117 ++
.../389-console/src/lib/monitor/auditlog.jsx | 117 ++
.../src/lib/monitor/chainingMonitor.jsx | 177 +++
.../389-console/src/lib/monitor/dbMonitor.jsx | 368 ++++++
.../389-console/src/lib/monitor/errorlog.jsx | 140 +++
.../389-console/src/lib/monitor/monitorModals.jsx | 559 +++++++++
.../389-console/src/lib/monitor/monitorTables.jsx | 1295 ++++++++++++++++++++
.../389-console/src/lib/monitor/replMonitor.jsx | 538 ++++++++
.../389-console/src/lib/monitor/serverMonitor.jsx | 161 +++
.../389-console/src/lib/monitor/snmpMonitor.jsx | 223 ++++
.../389-console/src/lib/monitor/suffixMonitor.jsx | 423 +++++++
src/cockpit/389-console/src/lib/notifications.jsx | 2 +-
src/cockpit/389-console/src/lib/tools.jsx | 47 +
src/cockpit/389-console/src/monitor.html | 916 --------------
src/cockpit/389-console/src/monitor.js | 398 ------
src/cockpit/389-console/src/monitor.jsx | 1187 ++++++++++++++++++
src/cockpit/389-console/src/plugins.jsx | 17 +-
src/cockpit/389-console/src/replication.html | 14 +-
src/cockpit/389-console/src/replication.js | 6 +-
src/cockpit/389-console/src/schema.html | 4 +-
src/cockpit/389-console/src/servers.html | 6 +-
src/cockpit/389-console/webpack.config.js | 2 -
src/lib389/cli/dsconf | 13 +-
src/lib389/lib389/_mapped_object.py | 15 +-
src/lib389/lib389/agreement.py | 85 +-
src/lib389/lib389/chaining.py | 14 +-
src/lib389/lib389/cli_base/__init__.py | 7 +-
src/lib389/lib389/cli_conf/backend.py | 52 +-
src/lib389/lib389/cli_conf/monitor.py | 88 ++
src/lib389/lib389/cli_conf/replication.py | 178 ++-
src/lib389/lib389/monitor.py | 83 +-
src/lib389/lib389/replica.py | 23 +-
46 files changed, 6116 insertions(+), 1617 deletions(-)
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(
- <Plugins serverId={serverIdElem} key={n} />,
+ <Plugins serverId={serverIdElem} key={tabKey} />,
document.getElementById("plugins")
);
// Database tab
ReactDOM.render(
- <Database serverId={serverIdElem} key={n} />,
+ <Database serverId={serverIdElem} key={tabKey} />,
document.getElementById("database")
);
+
+ // Monitor tab
+ ReactDOM.render(
+ <Monitor serverId={serverIdElem} key={tabKey} />,
+ 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 @@
<script src="servers.js"></script>
<script src="security.js"></script>
<script src="replication.js"></script>
- <script src="monitor.js"></script>
<link href="static/bootstrap.min.css" rel="stylesheet">
<link href="static/jquery.dataTables.min.css" type="text/css"
rel="stylesheet">
<link href="static/jquery.timepicker.min.css" type="text/css"
rel="stylesheet">
@@ -117,7 +116,7 @@
<li><a href="#0" class="ds-nav-choice"
id="repl-config-btn"
parent-id="replication-tab">Configuration</a></li>
<li><a href="#0" class="ds-nav-choice"
id="repl-agmts-btn"
parent-id="replication-tab">Agreements</a></li>
<li><a href="#0" class="ds-nav-choice"
id="repl-winsync-btn" parent-id="replication-tab">Winsync
Agreements</a></li>
- <li><a href="#0" class="ds-nav-choice"
id="repl-cleanallruv-btn" parent-id="replication-tab">CleanAllRUV
Tasks</a></li>
+ <li><a href="#0" class="ds-nav-choice"
id="repl-tasks-btn" parent-id="replication-tab">Replication
Tasks</a></li>
</ul>
</li>
@@ -142,26 +141,10 @@
</li>
<!-- Monitoring navtab -->
- <li class="dropdown ds-nav-tab">
- <a href="#0" class="dropdown-toggle ds-tab-list"
data-toggle="dropdown" id="monitor-tab">
+ <li class="ds-nav-tab">
+ <a href="#0" class="ds-tab-list
ds-tab-standalone"id="monitor-tab">
Monitoring
- <b class="caret"></b>
</a>
- <ul class="dropdown-menu ds-nav-item">
- <li><a tabindex="-1" id="monitor-db-btn"
class="ds-nav-choice" parent-id="monitor-tab"
href="#0">Database Performance</a></li>
- <li class="dropdown-submenu">
- <a tabindex="-1" href="#0">Logging</a>
- <ul class="dropdown-menu">
- <li><a href="#0" class="ds-nav-choice"
id="monitor-log-access-btn" parent-id="monitor-tab">Access
Log</a></li>
- <li><a href="#0" class="ds-nav-choice"
id="monitor-log-audit-btn" parent-id="monitor-tab">Audit
Log</a></li>
- <li><a href="#0" class="ds-nav-choice"
id="monitor-log-auditfail-btn" parent-id="monitor-tab">Audit
Failure Log</a></li>
- <li><a href="#0" class="ds-nav-choice"
id="monitor-log-errors-btn" parent-id="monitor-tab">Errors
Log</a></li>
- </ul>
- </li>
- <li><a tabindex="-1" class="ds-nav-choice"
parent-id="monitor-tab" id="monitor-repl-btn"
href="#0">Replication</a></li>
- <li><a tabindex="-1" class="ds-nav-choice"
parent-id="monitor-tab" id="monitor-server-btn"
href="#0">Server Statistics</a></li>
- <li><a tabindex="-1" class="ds-nav-choice"
parent-id="monitor-tab" id="monitor-snmp-btn"
href="#0">SNMP Counters</a></li>
- </ul>
</li>
</ul>
</div>
@@ -281,7 +264,7 @@
<p><span class="spinner spinner-xs
spinner-inline"></span> Reloading schema files...</p>
</div>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id
="schema-reload-btn">Reload Schema</button>
</div>
@@ -320,7 +303,7 @@
<p><span class="spinner spinner-xs
spinner-inline"></span> Restoring the server...</p>
</div>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-primary"
data-dismiss="modal">Close</button>
</div>
</div>
@@ -382,7 +365,7 @@
</div>
</form>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="chaining-save">Create Link</button>
</div>
@@ -455,7 +438,7 @@
</div>
</form>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="create-inst-save">Create Instance</button>
</div>
@@ -487,7 +470,7 @@
<p><span class="spinner spinner-xs
spinner-inline"></span> Backing up the server...</p>
</div>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="ds-backup-btn">Do Backup</button>
</div>
@@ -535,6 +518,7 @@
</div>
<div id="monitor-content" class="all-pages" hidden>
+ <div id="monitor"></div>
</div>
</div>
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 {
</div>
</Form>
</Modal.Body>
- <Modal.Footer className="ds-modal-footerZZZ">
+ <Modal.Footer>
<Button
bsStyle="default"
className="btn-cancel"
@@ -1221,7 +1228,7 @@ export class ChainCompsModal extends React.Component {
</div>
</Form>
</Modal.Body>
- <Modal.Footer className="ds-modal-footer">
+ <Modal.Footer>
<Button
bsStyle="default"
className="btn-cancel"
diff --git a/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
b/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
index a34316d..edb0557 100644
--- a/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
+++ b/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
@@ -190,10 +190,11 @@ export class GlobalDatabaseConfig extends React.Component {
);
})
.fail(err => {
+ let errMsg = JSON.parse(err);
this.props.reload();
this.props.addNotification(
"error",
- `Error updating configuration - ${err}`
+ `Error updating configuration - ${errMsg.desc}`
);
});
}
@@ -370,7 +371,7 @@ export class GlobalDatabaseConfig extends React.Component {
<p />
</div>
</CustomCollapse>
- <hr />
+ <p />
<div className="ds-save-btn">
<button className="btn btn-primary save-button"
onClick={this.save_db_config}>Save
Configuration</button>
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 =
+ <div>
+ <Spinner inline loading size="sm" />
+ Reloading access log...
+ </div>;
+ }
+ let contRefreshCheckbox =
+ <input type="checkbox" className="ds-sm-left-margin"
+ onChange={this.props.handleRefresh}
+ />;
+ if (this.props.refreshing) {
+ contRefreshCheckbox =
+ <input type="checkbox"
className="ds-sm-left-margin"
+ defaultChecked onChange={this.props.handleRefresh}
+ />;
+ }
+
+ let selectLines =
+ <div>
+ <label htmlFor="accesslog-lines"> Log Lines To
Show</label><select
+ className="btn btn-default dropdown ds-left-margin"
+ onChange={this.props.handleChange}
+ id="accesslog-lines" value={this.props.lines}>
+ <option>50</option>
+ <option>100</option>
+ <option>200</option>
+ <option>300</option>
+ <option>400</option>
+ <option>500</option>
+ <option>1000</option>
+ <option>2000</option>
+ <option>5000</option>
+ <option>10000</option>
+ <option>50000</option>
+ </select>
+ </div>;
+
+ return (
+ <div id="monitor-log-access-page"
className="container-fluid">
+ <Row>
+ <Col sm={3}>
+ <ControlLabel className="ds-suffix-header">
+ Access Log
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh access log"
+ onClick={() => this.props.reload(this.props.reload)}
+ />
+ </ControlLabel>
+ </Col>
+ <Col sm={9} className="ds-float-left">
+ {spinner}
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={6}>
+ {selectLines}
+ </Col>
+ <Col sm={6}>
+ <div className="ds-float-right">
+ <label>
+ {contRefreshCheckbox} Continuously Refresh
+ </label>
+ </div>
+ </Col>
+ </Row>
+ <textarea id="accesslog-area"
className="ds-logarea" value={this.props.data} readOnly />
+ </div>
+ );
+ }
+}
+
+// Props and defaultProps
+
+AccessLogMonitor.propTypes = {
+ data: PropTypes.string,
+ handleChange: PropTypes.func,
+ handleRefresh: PropTypes.func,
+ reload: PropTypes.func,
+ reloading: PropTypes.bool,
+ refreshing: PropTypes.bool,
+ lines: PropTypes.string,
+};
+
+AccessLogMonitor.defaultProps = {
+ data: "",
+ handleChange: noop,
+ handleRefresh: noop,
+ reload: noop,
+ reloading: false,
+ refreshing: false,
+ line: "50"
+};
+
+export default AccessLogMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/auditfaillog.jsx
b/src/cockpit/389-console/src/lib/monitor/auditfaillog.jsx
new file mode 100644
index 0000000..d533fcc
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/auditfaillog.jsx
@@ -0,0 +1,117 @@
+import React from "react";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import {
+ Spinner,
+ noop,
+ Row,
+ Col,
+ Icon,
+ ControlLabel
+} from "patternfly-react";
+
+export class AuditFailLogMonitor extends React.Component {
+ componentDidUpdate () {
+ // Set the textarea to be scrolled down to the bottom
+ let textarea = document.getElementById('auditfaillog-area');
+ textarea.scrollTop = textarea.scrollHeight;
+ }
+
+ render() {
+ let spinner = "";
+ if (this.props.reloading) {
+ spinner =
+ <div>
+ <Spinner inline loading size="sm" />
+ Reloading audit failure log...
+ </div>;
+ }
+ let contRefreshCheckbox =
+ <input type="checkbox" className="ds-sm-left-margin"
+ onChange={this.props.handleRefresh}
+ />;
+ if (this.props.refreshing) {
+ contRefreshCheckbox =
+ <input type="checkbox"
className="ds-sm-left-margin"
+ defaultChecked onChange={this.props.handleRefresh}
+ />;
+ }
+
+ let selectLines =
+ <div>
+ <label htmlFor="auditfaillog-lines"> Log Lines To
Show</label><select
+ className="btn btn-default dropdown ds-left-margin"
+ onChange={this.props.handleChange}
+ id="auditfaillog-lines" value={this.props.lines}>
+ <option>50</option>
+ <option>100</option>
+ <option>200</option>
+ <option>300</option>
+ <option>400</option>
+ <option>500</option>
+ <option>1000</option>
+ <option>2000</option>
+ <option>5000</option>
+ <option>10000</option>
+ <option>50000</option>
+ </select>
+ </div>;
+
+ return (
+ <div id="monitor-log-auditfail-page"
className="container-fluid">
+ <Row>
+ <Col sm={3}>
+ <ControlLabel className="ds-suffix-header">
+ Audit Failure Log
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh audit failure log"
+ onClick={() => this.props.reload(this.props.reload)}
+ />
+ </ControlLabel>
+ </Col>
+ <Col sm={9} className="ds-float-left">
+ {spinner}
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={6}>
+ {selectLines}
+ </Col>
+ <Col sm={6}>
+ <div className="ds-float-right">
+ <label>
+ {contRefreshCheckbox} Continuously Refresh
+ </label>
+ </div>
+ </Col>
+ </Row>
+ <textarea id="auditfaillog-area"
className="ds-logarea" value={this.props.data} readOnly />
+ </div>
+ );
+ }
+}
+
+// Props and defaultProps
+
+AuditFailLogMonitor.propTypes = {
+ data: PropTypes.string,
+ handleChange: PropTypes.func,
+ handleRefresh: PropTypes.func,
+ reload: PropTypes.func,
+ reloading: PropTypes.bool,
+ refreshing: PropTypes.bool,
+ lines: PropTypes.string,
+};
+
+AuditFailLogMonitor.defaultProps = {
+ data: "",
+ handleChange: noop,
+ handleRefresh: noop,
+ reload: noop,
+ reloading: false,
+ refreshing: false,
+ line: "50"
+};
+
+export default AuditFailLogMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/auditlog.jsx
b/src/cockpit/389-console/src/lib/monitor/auditlog.jsx
new file mode 100644
index 0000000..ae84c6b
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/auditlog.jsx
@@ -0,0 +1,117 @@
+import React from "react";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import {
+ Spinner,
+ noop,
+ Row,
+ Col,
+ Icon,
+ ControlLabel
+} from "patternfly-react";
+
+export class AuditLogMonitor extends React.Component {
+ componentDidUpdate () {
+ // Set the textarea to be scrolled down to the bottom
+ let textarea = document.getElementById('auditlog-area');
+ textarea.scrollTop = textarea.scrollHeight;
+ }
+
+ render() {
+ let spinner = "";
+ if (this.props.reloading) {
+ spinner =
+ <div>
+ <Spinner inline loading size="sm" />
+ Reloading audit log...
+ </div>;
+ }
+ let contRefreshCheckbox =
+ <input type="checkbox" className="ds-sm-left-margin"
+ onChange={this.props.handleRefresh}
+ />;
+ if (this.props.refreshing) {
+ contRefreshCheckbox =
+ <input type="checkbox"
className="ds-sm-left-margin"
+ defaultChecked onChange={this.props.handleRefresh}
+ />;
+ }
+
+ let selectLines =
+ <div>
+ <label htmlFor="auditlog-lines"> Log Lines To
Show</label><select
+ className="btn btn-default dropdown ds-left-margin"
+ onChange={this.props.handleChange}
+ id="auditlog-lines" value={this.props.lines}>
+ <option>50</option>
+ <option>100</option>
+ <option>200</option>
+ <option>300</option>
+ <option>400</option>
+ <option>500</option>
+ <option>1000</option>
+ <option>2000</option>
+ <option>5000</option>
+ <option>10000</option>
+ <option>50000</option>
+ </select>
+ </div>;
+
+ return (
+ <div id="monitor-log-audit-page"
className="container-fluid">
+ <Row>
+ <Col sm={3}>
+ <ControlLabel className="ds-suffix-header">
+ Audit Log
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh audit log"
+ onClick={() => this.props.reload(this.props.reload)}
+ />
+ </ControlLabel>
+ </Col>
+ <Col sm={9} className="ds-float-left">
+ {spinner}
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={6}>
+ {selectLines}
+ </Col>
+ <Col sm={6}>
+ <div className="ds-float-right">
+ <label>
+ {contRefreshCheckbox} Continuously Refresh
+ </label>
+ </div>
+ </Col>
+ </Row>
+ <textarea id="auditlog-area"
className="ds-logarea" value={this.props.data} readOnly />
+ </div>
+ );
+ }
+}
+
+// Props and defaultProps
+
+AuditLogMonitor.propTypes = {
+ data: PropTypes.string,
+ handleChange: PropTypes.func,
+ handleRefresh: PropTypes.func,
+ reload: PropTypes.func,
+ reloading: PropTypes.bool,
+ refreshing: PropTypes.bool,
+ lines: PropTypes.string,
+};
+
+AuditLogMonitor.defaultProps = {
+ data: "",
+ handleChange: noop,
+ handleRefresh: noop,
+ reload: noop,
+ reloading: false,
+ refreshing: false,
+ line: "50"
+};
+
+export default AuditLogMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/chainingMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/chainingMonitor.jsx
new file mode 100644
index 0000000..9c108ff
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/chainingMonitor.jsx
@@ -0,0 +1,177 @@
+import React from "react";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import {
+ Row,
+ Col,
+ ControlLabel,
+ Icon,
+} from "patternfly-react";
+
+export class ChainingMonitor extends React.Component {
+ render() {
+ return (
+ <div id="monitor-server-page"
className="container-fluid">
+ <Row>
+ <Col sm={12} className="ds-word-wrap">
+ <ControlLabel className="ds-suffix-header">
+ <Icon type="fa" name="link" />
{this.props.suffix} (<i>{this.props.bename}</i>)
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh chaining monitor"
+ onClick={() => this.props.reload(this.props.suffix)}
+ />
+ </ControlLabel>
+ </Col>
+ </Row>
+ <p />
+ <hr />
+ <div>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Add Operations
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nsaddcount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Delete Operations
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nsdeletecount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Modify Operations
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nsmodifycount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ ModRDN Operations
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nsrenamecount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Base Searches
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nssearchbasecount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ One-Level Searches
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nssearchonelevelcount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Subtree Searches
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nssearchsubtreecount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Abandon Operations
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nsabandoncount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Bind Operations
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nsbindcount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Unbind Operations
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nsunbindcount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Compare Operations
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nscomparecount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Outgoing Connections
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nsopenopconnectioncount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Outgoing Bind Connections
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.nsopenbindconnectioncount} size="35" readOnly />
+ </Col>
+ </Row>
+ </div>
+ </div>
+ );
+ }
+}
+
+ChainingMonitor.propTypes = {
+ suffix: PropTypes.string,
+ bename: PropTypes.string,
+ data: PropTypes.object
+};
+
+ChainingMonitor.defaultProps = {
+ suffix: "",
+ bename: "",
+ data: {}
+};
+
+export default ChainingMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/dbMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/dbMonitor.jsx
new file mode 100644
index 0000000..f5449dc
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/dbMonitor.jsx
@@ -0,0 +1,368 @@
+import React from "react";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import {
+ DonutChart,
+ PieChart,
+ Nav,
+ NavItem,
+ TabContent,
+ TabPane,
+ TabContainer,
+ Row,
+ Col,
+ ControlLabel,
+ Icon,
+ noop,
+} from "patternfly-react";
+import d3 from "d3";
+
+export class DatabaseMonitor extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = {
+ activeKey: 1,
+ };
+ this.handleNavSelect = this.handleNavSelect.bind(this);
+ }
+
+ handleNavSelect(key) {
+ this.setState({ activeKey: key });
+ }
+
+ render() {
+ let badColor = "#d01c8b";
+ let warningColor = "#ffc107";
+ let goodColor = "#4dac26";
+ let emptyColor = "#d3d3d3";
+ let donutColorDB = goodColor;
+ let donutColorNDN = goodColor;
+ let donutColorNDNUtil = goodColor;
+ let donutColorNDNMiss = emptyColor;
+ let donutColorDBMiss = emptyColor;
+ let dbcachehit = parseInt(this.props.data.dbcachehitratio[0]);
+ let ndncachehit = parseInt(this.props.data.normalizeddncachehitratio[0]);
+ let ndncachemax = parseInt(this.props.data.maxnormalizeddncachesize[0]);
+ let ndncachecurr = parseInt(this.props.data.currentnormalizeddncachesize[0]);
+ let utilratio = Math.round((ndncachecurr / ndncachemax) * 100);
+
+ // Database cache
+ if (dbcachehit > 89) {
+ donutColorDB = goodColor;
+ } else if (dbcachehit > 74) {
+ donutColorDB = warningColor;
+ } else {
+ if (dbcachehit < 50) {
+ // Pie chart shows higher catagory, so we need to highlight the misses
+ donutColorDBMiss = badColor;
+ } else {
+ donutColorDB = badColor;
+ }
+ }
+ // NDN cache ratio
+ if (ndncachehit > 89) {
+ donutColorNDN = goodColor;
+ } else if (ndncachehit > 74) {
+ donutColorNDN = warningColor;
+ } else {
+ if (ndncachehit < 50) {
+ // Pie chart shows higher catagory, so we need to highlight the misses
+ donutColorNDNMiss = badColor;
+ } else {
+ donutColorNDN = badColor;
+ }
+ }
+ // NDN cache utilization
+ if (ndncachehit < 90) {
+ if (utilratio > 95) {
+ donutColorNDNUtil = badColor;
+ } else if (utilratio > 90) {
+ donutColorNDNUtil = warningColor;
+ }
+ }
+
+ return (
+ <div id="db-content" className="container-fluid">
+ <Row>
+ <Col sm={12} className="ds-word-wrap">
+ <ControlLabel className="ds-suffix-header">
+ Database Performance Statistics
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh database monitor"
+ onClick={this.props.reload}
+ />
+ </ControlLabel>
+ </Col>
+ </Row>
+ <TabContainer id="basic-tabs-pf"
onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html: 'Database
Cache'}} />
+ </NavItem>
+ <NavItem eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html: 'Normalized
DN Cache'}} />
+ </NavItem>
+ </Nav>
+ <TabContent>
+
+ <TabPane eventKey={1}>
+ <div className="ds-margin-top-lg
ds-margin-left-piechart">
+ <DonutChart
+ id="monitor-db-cache-hitratio-chart"
+ size={{width: 180, height: 120}}
+ data={{
+ columns: [['miss', 100 - dbcachehit],
['hit', dbcachehit]],
+ colors: {
+ 'hit': donutColorDB,
+ 'miss': donutColorDBMiss,
+ },
+ order: null,
+ }}
+ title={{type: 'percent'}}
+ legend={{show: true, position: 'right'}}
+ />
+ <b className="ds-left-margin">DB
Cache Hit Ratio</b>
+ </div>
+ <hr />
+ <div>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Database Cache Hit Ratio
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.dbcachehitratio} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Database Cache Tries
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.dbcachetries} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Database Cache Hits
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.dbcachehits} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Cache Pages Read
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.dbcachepagein} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Cache Pages Written
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.dbcachepageout} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Read-Only Page Evictions
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.dbcacheroevict} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Read-Write Page Evictions
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.dbcacherwevict} size="28" readOnly />
+ </Col>
+ </Row>
+ </div>
+ </TabPane>
+
+ <TabPane eventKey={2}>
+ <div className="ds-margin-top-lg">
+ <div className="ds-container">
+ <div className="ds-left-margin">
+ <DonutChart
+
id="monitor-db-cache-ndn-hitratio-chart"
+ size={{width: 180, height: 120}}
+ data={{
+ columns: [['miss', 100 -
ndncachehit], ['hit', ndncachehit]],
+ colors: {
+ 'hit': donutColorNDN,
+ 'miss':
donutColorNDNMiss,
+ },
+ order: null,
+ }}
+ title={{type: 'percent'}}
+ legend={{show: true, position:
'right'}}
+ />
+ <b>NDN Cache Hit Ratio</b>
+ </div>
+ <div className="ds-chart-right">
+ <PieChart
+
id="monitor-db-cache-ndn-util-chart"
+ size={{width: 180, height: 120}}
+ data={{
+ columns: [
+ ['Used', utilratio],
+ ['Unused', 100 -
utilratio],
+ ],
+ colors: {
+ 'Used':
donutColorNDNUtil,
+ 'Unused': emptyColor,
+ },
+ order: null,
+ }}
+ pie={{
+ label: {
+ format: function (value, ratio,
id) {
+ return
d3.format(',%')(value / 100);
+ }
+ }
+ }}
+ title={{type: 'pie'}}
+ legend={{show: true, position:
'right'}}
+ />
+ <b>NDN Cache Utilization</b>
+ <div>
+ (DN's in cache:
<b>{this.props.data.currentnormalizeddncachecount}</b>)
+ </div>
+ </div>
+ </div>
+ <hr />
+ <div>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ NDN Cache Hit Ratio
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.normalizeddncachehitratio} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ NDN Cache Tries
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.normalizeddncachetries} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ NDN Cache Hits
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.normalizeddncachehits} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ NDN Cache Evictions
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.normalizeddncacheevictions} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ NDN Cache Max Size
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.maxnormalizeddncachesize} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ NDN Current Cache Size
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.currentnormalizeddncachesize} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ NDN Cache DN Count
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.currentnormalizeddncachecount} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ NDN Cache Thread Size
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.normalizeddncachethreadsize} size="28" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ NDN Cache Thread Slots
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.normalizeddncachethreadslots} size="28" readOnly />
+ </Col>
+ </Row>
+ </div>
+ </div>
+ </TabPane>
+ </TabContent>
+ </div>
+ </TabContainer>
+ </div>
+ );
+ }
+}
+
+// Prop types and defaults
+
+DatabaseMonitor.propTypes = {
+ data: PropTypes.object,
+ reload: PropTypes.func
+};
+
+DatabaseMonitor.defaultProps = {
+ data: {},
+ reload: noop
+};
+
+export default DatabaseMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/errorlog.jsx
b/src/cockpit/389-console/src/lib/monitor/errorlog.jsx
new file mode 100644
index 0000000..335a6d6
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/errorlog.jsx
@@ -0,0 +1,140 @@
+import React from "react";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import {
+ Spinner,
+ ControlLabel,
+ noop,
+ Icon,
+ Row,
+ Col
+} from "patternfly-react";
+
+export class ErrorLogMonitor extends React.Component {
+ componentDidUpdate () {
+ // Set the textarea to be scrolled down to the bottom
+ let textarea = document.getElementById('errorslog-area');
+ textarea.scrollTop = textarea.scrollHeight;
+ }
+
+ render() {
+ let spinner = "";
+ if (this.props.reloading) {
+ spinner =
+ <div>
+ <Spinner inline loading size="sm" />
+ Reloading errors log...
+ </div>;
+ }
+ let contRefreshCheckbox =
+ <input type="checkbox" className="ds-sm-left-margin"
+ onChange={this.props.handleRefresh}
+ />;
+ if (this.props.refreshing) {
+ contRefreshCheckbox =
+ <input type="checkbox"
className="ds-sm-left-margin"
+ defaultChecked onChange={this.props.handleRefresh}
+ />;
+ }
+
+ let selectLines =
+ <div>
+ <label htmlFor="errorlog-lines"> Log Lines To
Show</label><select
+ className="btn btn-default dropdown ds-left-margin"
+ onChange={this.props.handleChange}
+ id="errorlog-lines" value={this.props.lines}>
+ <option>50</option>
+ <option>100</option>
+ <option>200</option>
+ <option>300</option>
+ <option>400</option>
+ <option>500</option>
+ <option>1000</option>
+ <option>2000</option>
+ <option>5000</option>
+ <option>10000</option>
+ <option>50000</option>
+ </select>
+ </div>;
+
+ return (
+ <div id="monitor-log-errors-page"
className="container-fluid">
+ <Row>
+ <Col sm={3}>
+ <ControlLabel className="ds-suffix-header">
+ Errors Log
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh errors log"
+ onClick={() => this.props.reload(this.props.reload)}
+ />
+ </ControlLabel>
+ </Col>
+ <Col sm={9} className="ds-float-left">
+ {spinner}
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={5}>
+ {selectLines}
+ </Col>
+ <Col sm={4}>
+ <div className="dropdown">
+ <label
htmlFor="errorslog-sev-level">Filter</label><select
+ className="btn btn-default dropdown
ds-left-margin"
+ onChange={this.props.handleSevLevel}>
+ <option>Everything</option>
+ <option>Error Messages</option>
+ <option>Info Messages</option>
+ <option disabled>──────────</option>
+ <option>Emergency</option>
+ <option>Alert</option>
+ <option>Critical</option>
+ <option>Error</option>
+ <option>Warning</option>
+ <option>Notice</option>
+ <option>Info</option>
+ <option>Debug</option>
+ </select>
+ </div>
+ </Col>
+ <Col sm={3}>
+ <div className="ds-float-right">
+ <label>
+ {contRefreshCheckbox} Continuously Refresh
+ </label>
+ </div>
+ </Col>
+ </Row>
+ <textarea id="errorslog-area"
className="ds-logarea" value={this.props.data} readOnly />
+ </div>
+ );
+ }
+}
+
+// Props and defaultProps
+
+ErrorLogMonitor.propTypes = {
+ data: PropTypes.string,
+ handleChange: PropTypes.func,
+ reload: PropTypes.func,
+ reloading: PropTypes.bool,
+ handleSevLevel: PropTypes.func,
+ refreshing: PropTypes.bool,
+ handleRefresh: PropTypes.func,
+ lines: PropTypes.string
+
+};
+
+ErrorLogMonitor.defaultProps = {
+ data: "",
+ handleChange: noop,
+ reload: noop,
+ reloading: false,
+ handleSevLevel: noop,
+ refreshing: false,
+ handleRefresh: noop,
+ lines: "50"
+};
+
+export default ErrorLogMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
b/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
new file mode 100644
index 0000000..a63c67a
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
@@ -0,0 +1,559 @@
+import React from "react";
+import {
+ Modal,
+ Row,
+ Col,
+ ControlLabel,
+ Icon,
+ Button,
+ Form,
+ noop,
+ Spinner,
+} from "patternfly-react";
+import PropTypes from "prop-types";
+import { get_date_string } from "../tools.jsx";
+import { LagReportTable } from "./monitorTables.jsx";
+import "../../css/ds.css";
+
+class ReplLoginModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ doReport,
+ spinning,
+ error
+ } = this.props;
+
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row className="ds-margin-top">
+ <hr />
+ <div className="ds-modal-spinner">
+ <Spinner loading inline size="lg"
/>Authenticating to all the replicas ...
+ </div>
+ </Row>;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Replication Login Credentials
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <p>
+ In order to get the replication agreement lag times and
state the
+ authentication credentials to the remote replicas must be
provided.
+ This only works if the bind credentials used are valid on
all the
+ replicas.
+ </p>
+ <hr />
+ <Row>
+ <Col sm={3}>
+ <ControlLabel>
+ Bind DN
+ </ControlLabel>
+ </Col>
+ <Col sm={9}>
+ <input
+ className={error.binddn ?
"ds-input-auto-bad" : "ds-input-auto"}
+ onChange={handleChange}
defaultValue="cn=Directory Manager"
+ type="text" id="binddn"
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={3}>
+ <ControlLabel>
+ Password
+ </ControlLabel>
+ </Col>
+ <Col sm={9}>
+ <input
+ className={error.bindpw ?
"ds-input-auto-bad" : "ds-input-auto"}
+ onChange={handleChange} type="password"
id="bindpw"
+ />
+ </Col>
+ </Row>
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Close
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={doReport}
+ >
+ Get Report
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+class ReplLagReportModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ agmts,
+ pokeAgmt,
+ viewAgmt
+ } = this.props;
+
+ return (
+ <Modal backdrop="static"
contentClassName="ds-lag-report" show={showModal} onHide={closeHandler}>
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Replication Lag Report
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <LagReportTable
+ agmts={agmts}
+ pokeAgmt={pokeAgmt}
+ viewAgmt={viewAgmt}
+ />
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Close
+ </Button>
+ </Modal.Footer>
+ </Modal>
+ );
+ }
+}
+
+class TaskLogModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ logData,
+ } = this.props;
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Task Log
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <div>
+ <textarea className="ds-logarea"
value={logData} readOnly />
+ </div>
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Close
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+class AgmtDetailsModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ agmt,
+ } = this.props;
+
+ // Format status dates from agmt
+ let convertedDate = {};
+ let dateAttrs = ['last-update-start', 'last-update-end',
+ 'last-init-start', 'last-init-end'];
+ for (let attr of dateAttrs) {
+ if (agmt[attr] == "19700101000000Z") {
+ convertedDate[attr] = "Unavailable";
+ } else {
+ convertedDate[attr] = get_date_string(agmt[attr]);
+ }
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Replication Agreement Details ({agmt['agmt-name']})
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Replica</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['replica']} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Agreement
Enabled</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['replica-enabled']} readOnly
/>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Init
Started</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={convertedDate['last-init-start']}
readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Init
Ended</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={convertedDate['last-init-end']}
readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Initialization
Status</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <textarea value={agmt['last-init-status']}
rows="5" className="ds-agmt-textarea" readOnly />
+ </Col>
+ </Row>
+
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Replication In
Progress</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['update-in-progress']}
readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Changes
Sent</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['number-changes-sent']}
readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Update
Started</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22"
value={convertedDate['last-update-start']} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Update
Ended</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={convertedDate['last-update-end']}
readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Update
Status</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <textarea
value={agmt['last-update-status']} rows="5"
className="ds-agmt-textarea" readOnly />
+ </Col>
+ </Row>
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-primary ds-float-left"
+ onClick={this.props.initAgmt}
+ >
+ Initialize Agreement
+ </Button>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Close
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+class WinsyncAgmtDetailsModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ agmt,
+ } = this.props;
+
+ // Format status dates from agmt
+ let dateAttrs = ['last-update-start', 'last-update-end',
+ 'last-init-start', 'last-init-end'];
+ for (let attr of dateAttrs) {
+ if (agmt[attr] == "19700101000000Z") {
+ agmt[attr] = "Unavailable";
+ } else {
+ agmt[attr] = get_date_string(agmt[attr]);
+ }
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Replication Winsync Agreement Details
({agmt['agmt-name']})
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Windows
Replica</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['replica']} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Agreement
Enabled</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['replica-enabled']} readOnly
/>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Init
Started</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['last-init-start']} readOnly
/>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Init
Ended</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['last-init-end']} readOnly
/>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Initialization
Status</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <textarea value={agmt['last-init-status']}
rows="5" className="ds-agmt-textarea" readOnly />
+ </Col>
+ </Row>
+
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Replication In
Progress</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['update-in-progress']}
readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Changes
Sent</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['number-changes-sent']}
readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Update
Started</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['last-update-start']}
readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Update
Ended</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" size="22" value={agmt['last-update-end']} readOnly
/>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Last Update
Status</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <textarea
value={agmt['last-update-status']} rows="5"
className="ds-agmt-textarea" readOnly />
+ </Col>
+ </Row>
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-primary ds-float-left"
+ onClick={this.props.initAgmt}
+ >
+ Initialize Agreement
+ </Button>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Close
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+// Prototypes and defaultProps
+AgmtDetailsModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ agmt: PropTypes.object,
+ initAgmt: PropTypes.func,
+};
+
+AgmtDetailsModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ agmt: {},
+ initAgmt: noop,
+};
+
+WinsyncAgmtDetailsModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ agmt: PropTypes.object,
+ initAgmt: PropTypes.func,
+};
+
+WinsyncAgmtDetailsModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ agmt: {},
+ initAgmt: noop,
+};
+
+TaskLogModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ logData: PropTypes.string
+};
+
+TaskLogModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ agreement: "",
+};
+
+ReplLoginModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ doReport: PropTypes.func,
+ spinning: PropTypes.bool,
+ error: PropTypes.object,
+};
+
+ReplLoginModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ doReport: noop,
+ spinning: false,
+ error: {},
+};
+
+export {
+ TaskLogModal,
+ AgmtDetailsModal,
+ ReplLagReportModal,
+ ReplLoginModal,
+ WinsyncAgmtDetailsModal,
+};
diff --git a/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx
b/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx
new file mode 100644
index 0000000..592e514
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx
@@ -0,0 +1,1295 @@
+import React from "react";
+import {
+ Button,
+ DropdownButton,
+ MenuItem,
+ actionHeaderCellFormatter,
+ sortableHeaderCellFormatter,
+ tableCellFormatter,
+ noop
+} from "patternfly-react";
+import { DSTable } from "../dsTable.jsx";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import { get_date_string } from "../tools.jsx";
+
+class AbortCleanALLRUVTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ rowKey: "name",
+ columns: [
+ {
+ property: "name",
+ header: {
+ label: "Task Name",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "created",
+ header: {
+ label: "Created",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "rid",
+ header: {
+ label: "Replica ID",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "status",
+ header: {
+ label: "Status",
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "log",
+ header: {
+ props: {
+ index: 4,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 4
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData.name[0]}>
+ <Button
+ onClick={() => {
+
this.props.viewLog(this.props.viewLog(rowData.name));
+ }}
+ >
+ View Log
+ </Button>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ],
+ };
+
+ this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
+ } // Constructor
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "Abort CleanAllRUV Tasks",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ render() {
+ let rows = [];
+ for (let task of this.props.tasks) {
+ rows.push({
+ 'name': task.cn,
+ 'created': get_date_string(task.nstaskcreated),
+ 'rid': task['replica-id'],
+ 'status': task.nstaskstatus,
+ });
+ }
+ let taskTable;
+ if (rows.length < 1) {
+ taskTable =
+ <DSTable
+ noSearchBar
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "No Tasks"}]}
+ />;
+ } else {
+ taskTable =
+ <DSTable
+ noSearchBar
+ getColumns={this.getColumns}
+ rowKey={this.state.rowKey}
+ rows={rows}
+ toolBarPagination={[6, 12, 24, 48, 96]}
+ toolBarPaginationPerPage={6}
+ />;
+ }
+
+ return (
+ <div className="ds-margin-top-xlg">
+ {taskTable}
+ </div>
+ );
+ }
+}
+
+class CleanALLRUVTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ rowKey: "name",
+ columns: [
+ {
+ property: "name",
+ header: {
+ label: "Task Name",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "created",
+ header: {
+ label: "Created",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "rid",
+ header: {
+ label: "Replica ID",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "status",
+ header: {
+ label: "Status",
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "log",
+ header: {
+ props: {
+ index: 4,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 4
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData.name[0]}>
+ <Button
+ onClick={() => {
+ this.props.viewLog(rowData.name);
+ }}
+ >
+ View Log
+ </Button>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ],
+ };
+
+ this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
+ } // Constructor
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "CleanAllRUV Tasks",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ render() {
+ let rows = [];
+ for (let task of this.props.tasks) {
+ // task.attrs.nstaskcreated[0]
+ rows.push({
+ 'name': task.attrs.cn[0],
+ 'created': get_date_string(task.attrs.nstaskcreated[0]),
+ 'rid': task.attrs['replica-id'][0],
+ 'status': task.attrs.nstaskstatus[0],
+ });
+ }
+
+ let taskTable;
+ if (rows.length < 1) {
+ taskTable =
+ <DSTable
+ noSearchBar
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "No Tasks"}]}
+ />;
+ } else {
+ taskTable =
+ <DSTable
+ noSearchBar
+ getColumns={this.getColumns}
+ rowKey={this.state.rowKey}
+ rows={rows}
+ toolBarPagination={[6, 12, 24, 48, 96]}
+ toolBarPaginationPerPage={6}
+ />;
+ }
+ return (
+ <div className="ds-margin-top-xlg">
+ {taskTable}
+ </div>
+ );
+ }
+}
+
+class WinsyncAgmtTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ fieldsToSearch: ["agmt-name", "replica",
"replica-enabled"],
+ rowKey: "agmt-name",
+ columns: [
+ {
+ property: "agmt-name",
+ header: {
+ label: "Agreement",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "replica",
+ header: {
+ label: "Windows Replica",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "replica-enabled",
+ header: {
+ label: "Enabled",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "last-update-status",
+ header: {
+ label: "Update Status",
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "number-changes-sent",
+ header: {
+ label: "Changes Sent",
+ props: {
+ index: 4,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 4
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "actions",
+ header: {
+ props: {
+ index: 5,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 5
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData['agmt-name']}>
+ <DropdownButton
id={rowData['agmt-name']}
+ bsStyle="default"
title="Actions">
+ <MenuItem eventKey="1"
onClick={() => {
+
this.props.viewAgmt(rowData['agmt-name']);
+ }}>
+ View Agreement Details
+ </MenuItem>
+ <MenuItem eventKey="2"
onClick={() => {
+
this.props.pokeAgmt(rowData['agmt-name']);
+ }}>
+ Poke Agreement
+ </MenuItem>
+ </DropdownButton>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ]
+ };
+ this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
+ } // Constructor
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "Replication Winsync Agreements",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ render() {
+ let agmtTable;
+ if (this.props.agmts.length < 1) {
+ agmtTable =
+ <DSTable
+ noSearchBar
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "No agreements"}]}
+ />;
+ } else {
+ agmtTable =
+ <DSTable
+ getColumns={this.getColumns}
+ fieldsToSearch={this.state.fieldsToSearch}
+ toolBarSearchField={this.state.searchField}
+ rowKey={this.state.rowKey}
+ rows={this.props.agmts}
+ disableLoadingSpinner
+ toolBarPagination={[6, 12, 24, 48, 96]}
+ toolBarPaginationPerPage={6}
+ />;
+ }
+
+ return (
+ <div className="ds-margin-top-xlg">
+ {agmtTable}
+ </div>
+ );
+ }
+}
+
+class AgmtTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ fieldsToSearch: ["agmt-name", "replica",
"replica-enabled"],
+ rowKey: "agmt-name",
+ columns: [
+ {
+ property: "agmt-name",
+ header: {
+ label: "Agreement",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "replica",
+ header: {
+ label: "Replica",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "replica-enabled",
+ header: {
+ label: "Enabled",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "last-update-status",
+ header: {
+ label: "Update Status",
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "number-changes-sent",
+ header: {
+ label: "Changes Sent",
+ props: {
+ index: 4,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 4
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "actions",
+ header: {
+ props: {
+ index: 5,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 5
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData['agmt-name']}>
+ <DropdownButton
id={rowData['agmt-name']}
+ bsStyle="default"
title="Actions">
+ <MenuItem eventKey="1"
onClick={() => {
+
this.props.viewAgmt(rowData['agmt-name']);
+ }}>
+ View Agreement Details
+ </MenuItem>
+ <MenuItem eventKey="2"
onClick={() => {
+
this.props.pokeAgmt(rowData['agmt-name']);
+ }}>
+ Poke Agreement
+ </MenuItem>
+ </DropdownButton>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ]
+ };
+ this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
+ } // Constructor
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "Replication Agreements",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ render() {
+ let agmtTable = "";
+ if (this.props.agmts.length < 1) {
+ agmtTable =
+ <DSTable
+ noSearchBar
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "No agreements"}]}
+ />;
+ } else {
+ agmtTable =
+ <DSTable
+ getColumns={this.getColumns}
+ rowKey={this.state.rowKey}
+ rows={this.props.agmts}
+ toolBarPagination={[6, 12, 24, 48, 96]}
+ toolBarPaginationPerPage={6}
+ />;
+ }
+ return (
+ <div className="ds-margin-top-xlg">
+ {agmtTable}
+ </div>
+ );
+ }
+}
+
+class ConnectionTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ rowKey: "connid",
+ columns: [
+ {
+ property: "date",
+ header: {
+ label: "Connection Opened",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "ip",
+ header: {
+ label: "IP Address",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "connid",
+ header: {
+ label: "ID",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "opStarted",
+ header: {
+ label: "Ops Started",
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "opCompleted",
+ header: {
+ label: "Ops Finished",
+ props: {
+ index: 4,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 4
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "binddn",
+ header: {
+ label: "Bind DN",
+ props: {
+ index: 5,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 5
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "readwrite",
+ header: {
+ label: "Read/Write",
+ props: {
+ index: 6,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 6
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ],
+ };
+
+ this.getColumns = this.getColumns.bind(this);
+ } // Constructor
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ render() {
+ // connection: %s:%s:%s:%s:%s:%s:%s:%s:%s:%s
+ //
+ // parts:
+ // 0 = file descriptor
+ // 1 = connection start date
+ // 2 = ops initiated
+ // 3 = ops completed
+ // 4 = r/w blocked
+ // 5 = bind DN
+ // 6 = connection is currently at max threads (1 = yes, 0 = no)
+ // 7 = number of times connection hit max threads
+ // 8 = number of operations blocked by max threads
+ // 9 = connection ID
+ // 10 = IP address (ip=###################)
+ //
+ // This is too many items to fit in the table, we have to pick and choose
+ // what "we" think are the most useful stats...
+
+ let rows = [];
+ for (let conn of this.props.conns) {
+ let parts = conn.split(':');
+
+ // Process the IP address
+ let ip = parts[10].replace("ip=", "");
+ if (ip == "local") {
+ ip = "LDAPI";
+ }
+ rows.push({
+ 'date': [get_date_string(parts[1])],
+ 'ip': [ip],
+ 'connid': [parts[9]],
+ 'opStarted': [parts[2]],
+ 'opCompleted': [parts[3]],
+ 'binddn': [parts[5]],
+ 'readwrite': [parts[4]]
+ });
+ }
+
+ return (
+ <div className="ds-margin-top-xlg">
+ <DSTable
+ getColumns={this.getColumns}
+ rowKey={this.state.rowKey}
+ rows={rows}
+ toolBarPagination={[6, 12, 24, 48, 96]}
+ toolBarPaginationPerPage={12}
+ />
+ </div>
+ );
+ }
+}
+
+class LagReportTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ rowKey: "agmt-name",
+ columns: [
+ {
+ property: "agmt-name",
+ header: {
+ label: "Agreement",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "replica-enabled",
+ header: {
+ label: "Enabled",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "replication-status",
+ header: {
+ label: "State",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true,
+ style: {
+ color: 'blue'
+ }
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2,
+ },
+ formatters: [tableCellFormatter],
+ },
+ },
+ {
+ property: "replication-lag-time",
+ header: {
+ label: "Lag Time",
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true,
+ style: {
+ color: 'blue'
+ },
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "actions",
+ header: {
+ props: {
+ index: 4,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 4
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData['agmt-name']}>
+ <DropdownButton
id={rowData['agmt-name']}
+ bsStyle="default"
title="Actions">
+ <MenuItem eventKey="1"
onClick={() => {
+
this.props.viewAgmt(rowData['agmt-name']);
+ }}>
+ View Agreement Details
+ </MenuItem>
+ <MenuItem eventKey="2"
onClick={() => {
+
this.props.pokeAgmt(rowData['agmt-name']);
+ }}>
+ Poke Agreement
+ </MenuItem>
+ </DropdownButton>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ]
+ };
+ this.getColumns = this.getColumns.bind(this);
+ } // Constructor
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ render() {
+ return (
+ <div>
+ <DSTable
+ noSearchBar
+ getColumns={this.getColumns}
+ rowKey={this.state.rowKey}
+ rows={this.props.agmts}
+ toolBarPagination={[6, 12, 24, 48, 96]}
+ toolBarPaginationPerPage={6}
+ />
+ </div>
+ );
+ }
+}
+
+// Proptypes and defaults
+
+LagReportTable.propTypes = {
+ agmts: PropTypes.array,
+ viewAgmt: PropTypes.func,
+ pokeAgmt: PropTypes.func,
+};
+
+LagReportTable.defaultProps = {
+ agmts: [],
+ viewAgmt: noop,
+ pokeAgmt: noop
+};
+
+AgmtTable.propTypes = {
+ agmts: PropTypes.array,
+ viewAgmt: PropTypes.func,
+ pokeAgmt: PropTypes.func,
+};
+
+AgmtTable.defaultProps = {
+ agmts: [],
+ viewAgmt: noop,
+ pokeAgmt: noop
+};
+
+// Proptyes and defaults
+WinsyncAgmtTable.propTypes = {
+ agmts: PropTypes.array,
+ viewAgmt: PropTypes.func,
+ pokeAgmt: PropTypes.func,
+};
+
+WinsyncAgmtTable.defaultProps = {
+ agmts: [],
+ viewAgmt: noop,
+ pokeAgmt: noop
+};
+
+ConnectionTable.propTypes = {
+ conns: PropTypes.array,
+};
+
+ConnectionTable.defaultProps = {
+ conns: [],
+};
+
+CleanALLRUVTable.propTypes = {
+ tasks: PropTypes.array,
+ viewLog: PropTypes.func,
+};
+
+CleanALLRUVTable.defaultProps = {
+ tasks: [],
+ viewLog: PropTypes.func,
+};
+
+AbortCleanALLRUVTable.propTypes = {
+ tasks: PropTypes.array,
+ viewLog: PropTypes.func,
+};
+
+AbortCleanALLRUVTable.defaultProps = {
+ tasks: [],
+ viewLog: PropTypes.func,
+};
+
+export {
+ ConnectionTable,
+ AgmtTable,
+ WinsyncAgmtTable,
+ LagReportTable,
+ CleanALLRUVTable,
+ AbortCleanALLRUVTable,
+};
diff --git a/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
new file mode 100644
index 0000000..a742a0d
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
@@ -0,0 +1,538 @@
+import React from "react";
+import cockpit from "cockpit";
+import { log_cmd } from "../tools.jsx";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import { ConfirmPopup } from "../notifications.jsx";
+import {
+ AgmtTable,
+ WinsyncAgmtTable,
+ CleanALLRUVTable,
+ AbortCleanALLRUVTable
+} from "./monitorTables.jsx";
+import {
+ TaskLogModal,
+ AgmtDetailsModal,
+ WinsyncAgmtDetailsModal,
+ ReplLagReportModal,
+ ReplLoginModal,
+} from "./monitorModals.jsx";
+import {
+ Nav,
+ NavItem,
+ TabContent,
+ TabPane,
+ TabContainer,
+ Button,
+ noop
+} from "patternfly-react";
+
+export class ReplMonitor extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = {
+ activeKey: 1,
+ logData: "",
+ showBindModal: false,
+ showLogModal: false,
+ showAgmtModal: false,
+ showWinsyncAgmtModal: false,
+ showInitWinsyncConfirm: false,
+ showInitConfirm: false,
+ showLoginModal: false,
+ showLagReport: false,
+ reportLoading: false,
+ lagAgmts: [],
+ agmt: "",
+ binddn: "cn=Directory Manager",
+ bindpw: "",
+ errObj: {}
+ };
+
+ this.handleNavSelect = this.handleNavSelect.bind(this);
+ this.pokeAgmt = this.pokeAgmt.bind(this);
+ this.initAgmt = this.initAgmt.bind(this);
+ this.initWinsyncAgmt = this.initWinsyncAgmt.bind(this);
+ this.confirmInit = this.confirmInit.bind(this);
+ this.confirmWinsyncInit = this.confirmWinsyncInit.bind(this);
+ this.closeInitConfirm = this.closeInitConfirm.bind(this);
+ this.closeInitWinsyncConfirm = this.closeInitWinsyncConfirm.bind(this);
+ this.pokeWinsyncAgmt = this.pokeWinsyncAgmt.bind(this);
+ this.showAgmtModal = this.showAgmtModal.bind(this);
+ this.closeAgmtModal = this.closeAgmtModal.bind(this);
+ this.showWinsyncAgmtModal = this.showWinsyncAgmtModal.bind(this);
+ this.closeWinsyncAgmtModal = this.closeWinsyncAgmtModal.bind(this);
+ this.getLagReportCreds = this.getLagReportCreds.bind(this);
+ this.doLagReport = this.doLagReport.bind(this);
+ this.closeLagReport = this.closeLagReport.bind(this);
+ this.viewCleanLog = this.viewCleanLog.bind(this);
+ this.viewAbortLog = this.viewAbortLog.bind(this);
+ this.closeLogModal = this.closeLogModal.bind(this);
+ this.handleLoginModal = this.handleLoginModal.bind(this);
+ this.closeLoginModal = this.closeLoginModal.bind(this);
+ }
+
+ handleNavSelect(key) {
+ this.setState({
+ activeKey: key
+ });
+ }
+
+ closeLogModal() {
+ this.setState({
+ showLogModal: false
+ });
+ }
+
+ viewCleanLog (name) {
+ let logData = "";
+ for (let task of this.props.data.cleanTasks) {
+ if (task.attrs.cn[0] == name) {
+ logData = task.attrs.nstasklog[0];
+ break;
+ }
+ }
+ this.setState({
+ showLogModal: true,
+ logData: logData
+ });
+ }
+
+ viewAbortLog (name) {
+ let logData = "";
+ for (let task of this.props.data.abortTasks) {
+ if (task.attrs.cn[0] == name) {
+ logData = task.attrs.nstasklog[0];
+ break;
+ }
+ }
+ this.setState({
+ showLogModal: true,
+ logData: logData
+ });
+ }
+
+ pokeAgmt (name) {
+ let cmd = ["dsconf", "-j",
"ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-agmt", "poke", "--suffix=" +
this.props.suffix, name];
+ log_cmd("pokeAgmt", "Awaken the agreement", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.addNotification(
+ "success",
+ `Replication agreement has been poked`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to poke replication agreement ${name} - ${errMsg.desc}`
+ );
+ });
+ }
+
+ pokeWinsyncAgmt(name) {
+ let cmd = ["dsconf", "-j",
"ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-winsync-agmt", "poke", "--suffix=" +
this.props.suffix, name];
+ log_cmd("pokeAgmt", "Awaken the agreement", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.addNotification(
+ "success",
+ `Replication winsync agreement has been poked`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to poke replication winsync agreement ${name} -
${errMsg.desc}`
+ );
+ });
+ }
+
+ showAgmtModal (name) {
+ for (let agmt of this.props.data.replAgmts) {
+ if (agmt['agmt-name'] == name) {
+ this.setState({
+ showAgmtModal: true,
+ agmt: agmt
+ });
+ break;
+ }
+ }
+ }
+
+ closeAgmtModal() {
+ this.setState({
+ showAgmtModal: false,
+ });
+ }
+
+ showWinsyncAgmtModal(name) {
+ for (let agmt of this.props.data.replWinsyncAgmts) {
+ if (agmt['agmt-name'] == name) {
+ this.setState({
+ showWinsyncAgmtModal: true,
+ agmt: agmt
+ });
+ break;
+ }
+ }
+ }
+
+ closeWinsyncAgmtModal() {
+ this.setState({
+ showWinsyncAgmtModal: false,
+ });
+ }
+
+ confirmInit() {
+ this.setState({
+ showInitConfirm: true,
+ });
+ }
+
+ closeInitConfirm() {
+ this.setState({
+ showInitConfirm: false
+ });
+ }
+
+ confirmWinsyncInit() {
+ this.setState({
+ showInitWinsyncConfirm: true
+ });
+ }
+
+ closeInitWinsyncConfirm() {
+ this.setState({
+ showInitWinsyncConfirm: false
+ });
+ }
+
+ initAgmt() {
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-agmt', 'init', '--suffix=' + this.props.suffix,
this.state.agmt['agmt-name'] ];
+ log_cmd('initAgmt', 'Initialize agreement', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reloadAgmts();
+ this.props.addNotification(
+ "success",
+ `Replication agreement initialization has started ...`
+ );
+ this.setState({
+ showAgmtModal: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to start agreement initialization - ${errMsg.desc}`
+ );
+ this.setState({
+ showAgmtModal: false
+ });
+ });
+ }
+
+ initWinsyncAgmt() {
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-winsync-agmt', 'init', '--suffix=' +
this.props.suffix, this.state.agmt['agmt-name'] ];
+ log_cmd('initWinsyncAgmt', 'Initialize winsync agreement', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reloadWinsyncAgmts();
+ this.props.addNotification(
+ "success",
+ `Replication winsync agreement initialization has started ...`
+ );
+ this.setState({
+ showInitWinsyncConfirm: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to start winsync agreement initialization -
${errMsg.desc}`
+ );
+ this.setState({
+ showInitWinsyncConfirm: false
+ });
+ });
+ }
+
+ getLagReportCreds () {
+ if (this.props.data.replAgmts.length == 0) {
+ // No agreements, don't proceed...
+ this.props.addNotification(
+ "error", "There are no replication agreements to report
on"
+ );
+ } else {
+ this.setState({
+ showLoginModal: true,
+ errObj: {
+ bindpw: true
+ }
+ });
+ }
+ }
+
+ closeLoginModal () {
+ this.setState({
+ showLoginModal: false,
+ });
+ }
+
+ handleLoginModal(e) {
+ const value = e.target.value.trim();
+ let valueErr = false;
+ let errObj = this.state.errObj;
+ if (value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj
+ });
+ }
+
+ closeLagReport() {
+ this.setState({
+ showLagReport: false
+ });
+ }
+
+ doLagReport() {
+ // Get agmts but this time with with bind credentials, then clear
+ // out bind credentials after we use them
+
+ if (this.state.binddn == "" || this.state.bindpw == "") {
+ return;
+ }
+
+ this.setState({
+ loginSpinning: true,
+ });
+
+ let cmd = ["dsconf", "-j",
"ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "replication", "status", "--suffix=" +
this.props.suffix,
+ "--bind-dn=" + this.state.binddn, "--bind-passwd=" +
this.state.bindpw];
+ log_cmd("doLagReport", "Get agmts for lag report", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let config = JSON.parse(content);
+ this.setState({
+ lagAgmts: config.items,
+ showLagReport: true,
+ showLoginModal: false,
+ loginSpinning: false,
+ bindpw: ""
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to get replication status - ${errMsg.desc}`
+ );
+ this.setState({
+ showLoginModal: false,
+ loginSpinning: false,
+ bindpw: ""
+ });
+ });
+ }
+
+ render() {
+ let replAgmts = this.props.data.replAgmts;
+ let replWinsyncAgmts = this.props.data.replWinsyncAgmts;
+ let cleanTasks = this.props.data.cleanTasks;
+ let abortTasks = this.props.data.abortTasks;
+ let agmtDetailModal = "";
+ let winsyncAgmtDetailModal = "";
+
+ if (this.state.showAgmtModal) {
+ agmtDetailModal =
+ <AgmtDetailsModal
+ showModal={this.state.showAgmtModal}
+ closeHandler={this.closeAgmtModal}
+ agmt={this.state.agmt}
+ initAgmt={this.confirmInit}
+ />;
+ }
+
+ if (this.state.showWinsyncAgmtModal) {
+ winsyncAgmtDetailModal =
+ <WinsyncAgmtDetailsModal
+ showModal={this.state.showWinsyncAgmtModal}
+ closeHandler={this.closeWinsyncAgmtModal}
+ agmt={this.state.agmt}
+ initAgmt={this.confirmWinsyncInit}
+ />;
+ }
+
+ let cleanNavTitle = 'CleanAllRUV Tasks <font size="1">('
+ cleanTasks.length + ')</font>';
+ let abortNavTitle = 'Abort CleanAllRUV Tasks <font
size="1">(' + abortTasks.length + ')</font>';
+ let taskContent =
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem className="ds-nav-med" eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html: cleanNavTitle}} />
+ </NavItem>
+ <NavItem className="ds-nav-med" eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html: abortNavTitle}} />
+ </NavItem>
+ </Nav>
+ <TabContent>
+ <TabPane eventKey={1}>
+ <div className="ds-indent ds-margin-top-lg">
+ <CleanALLRUVTable
+ tasks={cleanTasks}
+ viewLog={this.viewCleanLog}
+ />
+ </div>
+ </TabPane>
+ <TabPane eventKey={2}>
+ <div className="ds-indent ds-margin-top-lg">
+ <AbortCleanALLRUVTable
+ tasks={abortTasks}
+ viewLog={this.viewAbortLog}
+ />
+ </div>
+ </TabPane>
+ </TabContent>
+ </div>;
+
+ let AgmtNavTitle = 'Agreements <font size="1">(' +
replAgmts.length + ')</font>';
+ let WinsyncAgmtNavTitle = 'Winsync Agreements <font
size="1">(' + replWinsyncAgmts.length + ')</font>';
+ let TasksNavTitle = 'Tasks <font size="1">(' +
(cleanTasks.length + abortTasks.length) + ')</font>';
+
+ return (
+ <div id="monitor-suffix-page" className="container-fluid
ds-tab-table">
+ <TabContainer id="basic-tabs-pf"
onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html: AgmtNavTitle}}
/>
+ </NavItem>
+ <NavItem eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html:
WinsyncAgmtNavTitle}} />
+ </NavItem>
+ <NavItem eventKey={3}>
+ <div dangerouslySetInnerHTML={{__html: TasksNavTitle}}
/>
+ </NavItem>
+ </Nav>
+ <TabContent>
+ <TabPane eventKey={1}>
+ <div className="ds-margin-top-lg">
+ <AgmtTable
+ agmts={replAgmts}
+ pokeAgmt={this.pokeAgmt}
+ viewAgmt={this.showAgmtModal}
+ />
+ <p />
+ <Button
+ bsStyle="primary"
+ onClick={this.getLagReportCreds}
+ title="Display report that shows the lag
time and replication status of each agreement in relationship to its replica"
+ >
+ Get Lag Report
+ </Button>
+ </div>
+ </TabPane>
+
+ <TabPane eventKey={2}>
+ <div className="ds-margin-top-lg">
+ <WinsyncAgmtTable
+ agmts={replWinsyncAgmts}
+ pokeAgmt={this.pokeWinsyncAgmt}
+ viewAgmt={this.showWinsyncAgmtModal}
+ />
+ </div>
+ </TabPane>
+
+ <TabPane eventKey={3}>
+ <div className="ds-indent ds-tab-table">
+ <TabContainer id="task-tabs"
defaultActiveKey={1}>
+ {taskContent}
+ </TabContainer>
+ </div>
+ </TabPane>
+ </TabContent>
+ </div>
+ </TabContainer>
+ <TaskLogModal
+ showModal={this.state.showLogModal}
+ closeHandler={this.closeLogModal}
+ logData={this.state.logData}
+ />
+ <ConfirmPopup
+ showModal={this.state.showInitConfirm}
+ closeHandler={this.closeInitConfirm}
+ actionFunc={this.initAgmt}
+ actionParam={this.state.agmt['agmt-name']}
+ msg="Are you really sure you want to reinitialize this
replication agreement?"
+ msgContent={this.state.agmt['agmt-name']}
+ />
+ <ConfirmPopup
+ showModal={this.state.showInitWinsyncConfirm}
+ closeHandler={this.closeInitWinsyncConfirm}
+ actionFunc={this.initWinsyncAgmt}
+ actionParam={this.state.agmt['agmt-name']}
+ msg="Are you really sure you want to reinitialize this
replication winsync agreement?"
+ msgContent={this.state.agmt['agmt-name']}
+ />
+ <ReplLoginModal
+ showModal={this.state.showLoginModal}
+ closeHandler={this.closeLoginModal}
+ handleChange={this.handleLoginModal}
+ doReport={this.doLagReport}
+ spinning={this.state.loginSpinning}
+ error={this.state.errObj}
+ />
+ <ReplLagReportModal
+ showModal={this.state.showLagReport}
+ closeHandler={this.closeLagReport}
+ agmts={this.state.lagAgmts}
+ pokeAgmt={this.pokeAgmt}
+ viewAgmt={this.showAgmtModal}
+ />
+ {agmtDetailModal}
+ {winsyncAgmtDetailModal}
+ </div>
+ );
+ }
+}
+
+// Props and defaultProps
+
+ReplMonitor.propTypes = {
+ data: PropTypes.object,
+ suffix: PropTypes.string,
+ serverId: PropTypes.string,
+ addNotification: PropTypes.func,
+ reloadAgmts: PropTypes.func,
+ reloadWinsyncAgmts: PropTypes.func,
+};
+
+ReplMonitor.defaultProps = {
+ data: {},
+ suffix: "",
+ serverId: "",
+ addNotification: noop,
+ reloadAgmts: noop,
+ reloadWinsyncAgmts: noop,
+};
+
+export default ReplMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx
new file mode 100644
index 0000000..503b57d
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx
@@ -0,0 +1,161 @@
+import React from "react";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import { get_date_string, get_date_diff } from "../tools.jsx";
+import {
+ ConnectionTable
+} from "./monitorTables.jsx";
+import {
+ Nav,
+ NavItem,
+ TabContent,
+ TabPane,
+ TabContainer,
+ Row,
+ Col,
+ ControlLabel,
+ Icon,
+ noop
+} from "patternfly-react";
+
+export class ServerMonitor extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = {
+ activeKey: 1,
+ };
+ this.handleNavSelect = this.handleNavSelect.bind(this);
+ }
+
+ handleNavSelect(key) {
+ this.setState({ activeKey: key });
+ }
+
+ render() {
+ // Generate start time and Uptime
+ let startTime = this.props.data.starttime[0];
+ let currTime = this.props.data.currenttime[0];
+ let startDate = get_date_string(this.props.data.starttime[0]);
+ let uptime = get_date_diff(startTime, currTime);
+
+ return (
+ <div id="monitor-server-page"
className="container-fluid">
+ <Row>
+ <Col sm={12} className="ds-word-wrap">
+ <ControlLabel className="ds-suffix-header">
+ Server Statistics
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh server monitor"
+ onClick={this.props.reload}
+ />
+ </ControlLabel>
+ </Col>
+ </Row>
+
+ <TabContainer id="server-tabs-pf"
onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html: 'Server
Information'}} />
+ </NavItem>
+ <NavItem eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html: 'Connection
Table'}} />
+ </NavItem>
+ </Nav>
+ <TabContent>
+
+ <TabPane eventKey={1}>
+ <div className="ds-container
ds-margin-top-lg">
+ <div className="ds-inline">
+ <div>
+ <label
htmlFor="monitor-serverid" className="ds-label-xsm">Server
Instance</label><input type="text"
+ className="ds-input"
id="monitor-serverid" value={"slapd-" + this.props.serverId}
size="50" readOnly />
+ </div>
+ <div>
+ <label htmlFor="monitor-version"
className="ds-label-xsm">Version</label><input
type="text"
+ className="ds-input"
id="monitor-version" value={this.props.data.version} size="50"
readOnly />
+ </div>
+ <div>
+ <label
htmlFor="monitor-server-starttime" className="ds-label-xsm">Server
Started</label><input type="text"
+ className="ds-input"
id="monitor-server-starttime" value={startDate} size="50" readOnly
/>
+ </div>
+ <div>
+ <label
htmlFor="monitor-server-uptime" className="ds-label-xsm">Server
Uptime</label><input type="text"
+ className="ds-input"
id="monitor-server-uptime" value={uptime} size="50" readOnly />
+ </div>
+ </div>
+ </div>
+ <hr />
+ <div className="ds-container">
+ <div className="ds-inline">
+ <div>
+ <label
htmlFor="monitor-server-threads"
className="ds-monitor-label">Threads</label><input
type="text"
+ className="ds-input"
id="monitor-server-threads" value={this.props.data.threads} size="12"
readOnly />
+ </div>
+ <div>
+ <label
htmlFor="monitor-server-totalconnections"
className="ds-monitor-label">Total Connections</label><input
type="text"
+ className="ds-input"
id="monitor-server-totalconnections" value={this.props.data.totalconnections}
size="12" readOnly />
+ </div>
+ <div>
+ <label
htmlFor="monitor-server-currentconnections"
className="ds-monitor-label">Current Conections</label><input
type="text"
+ className="ds-input"
id="monitor-server-currentconnections"
value={this.props.data.currentconnections} size="12" readOnly />
+ </div>
+ <div>
+ <label
htmlFor="monitor-server-currentconnectionsatmaxthreads"
className="ds-monitor-label">Conns At Max Threads</label><input
type="text"
+ className="ds-input"
id="monitor-server-currentconnectionsatmaxthreads"
value={this.props.data.currentconnectionsatmaxthreads} size="12" readOnly />
+ </div>
+ <div>
+ <label
htmlFor="monitor-server-maxthreadsperconnhits"
className="ds-monitor-label">Conns Hit Max Threads</label><input
type="text"
+ className="ds-input"
id="monitor-server-maxthreadsperconnhits"
value={this.props.data.maxthreadsperconnhits} size="12" readOnly />
+ </div>
+ </div>
+ <div className="ds-divider" />
+ <div className="ds-inline">
+ <div>
+ <label
htmlFor="monitor-server-readwaiters"
className="ds-monitor-label-med">Threads Waiting To
Read</label><input type="text"
+ className="ds-input"
id="monitor-server-readwaiters" value={this.props.data.readwaiters}
size="12" readOnly />
+ </div>
+ <div>
+ <label
htmlFor="monitor-server-opsinitiated"
className="ds-monitor-label-med">Operations Started</label><input
type="text"
+ className="ds-input"
id="monitor-server-opsinitiated" value={this.props.data.opsinitiated}
size="12" readOnly />
+ </div>
+ <div>
+ <label
htmlFor="monitor-server-opscompleted"
className="ds-monitor-label-med">Operations Completed</label><input
type="text"
+ className="ds-input"
id="monitor-server-opscompleted" value={this.props.data.opscompleted}
size="12" readOnly />
+ </div>
+ <div>
+ <label
htmlFor="monitor-server-entriessent"
className="ds-monitor-label-med">Entries Returned To
Clients</label><input type="text"
+ className="ds-input"
id="monitor-server-entriessent" value={this.props.data.entriessent}
size="12" readOnly />
+ </div>
+ <div>
+ <label
htmlFor="monitor-server-bytessent"
className="ds-monitor-label-med">Bytes Sent to Clients</label><input
type="text"
+ className="ds-input"
id="monitor-server-bytessent" value={this.props.data.bytessent}
size="12" readOnly />
+ </div>
+
+ </div>
+ </div>
+ </TabPane>
+ <TabPane eventKey={2}>
+ <ConnectionTable conns={this.props.data.connection}
/>
+ </TabPane>
+ </TabContent>
+ </div>
+ </TabContainer>
+ </div>
+ );
+ }
+}
+
+ServerMonitor.propTypes = {
+ serverId: PropTypes.string,
+ data: PropTypes.object,
+ reload: PropTypes.func
+};
+
+ServerMonitor.defaultProps = {
+ serverId: "",
+ data: {},
+ reload: noop
+};
+
+export default ServerMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/snmpMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/snmpMonitor.jsx
new file mode 100644
index 0000000..fd1a4f1
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/snmpMonitor.jsx
@@ -0,0 +1,223 @@
+import React from "react";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import {
+ Row,
+ Col,
+ Icon,
+ ControlLabel,
+ noop
+} from "patternfly-react";
+
+export class SNMPMonitor extends React.Component {
+ render() {
+ return (
+ <div className="container-fluid"
id="db-global-page">
+ <Row>
+ <Col sm={12} className="ds-word-wrap">
+ <ControlLabel className="ds-suffix-header">
+ SNMP Counters
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh SNMP monitor"
+ onClick={this.props.reload}
+ />
+ </ControlLabel>
+ </Col>
+ </Row>
+ <div className="ds-margin-top-med">
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Anonymous Binds</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.anonymousbinds} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Referrals</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.referrals} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Unauthenticated
Binds</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.anonymousbinds} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Returned Referrals</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.referralsreturned} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Simple Auth Binds</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.simpleauthbinds} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Bind Security
Errors</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.bindsecurityerrors} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Strong Auth Binds</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.strongauthbinds} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Security Errors</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.securityerrors} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Initiated
Operations</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text" value={this.props.data.inops}
size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Errors</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.errors} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Compare Operations</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.compareops} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Current Connections</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.connections} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Add Operations</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.addentryops} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Total Connections</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.connectionseq} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Delete Operations</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.removeentryops} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Conns in Max
Threads</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.connectionsinmaxthreads} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Modify Operation</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.modifyentryops} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Conns Hit Max
Threads</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.connectionsmaxthreadscount} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>ModRDN Operations</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.modifyrdnops} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Bytes Received</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.bytesrecv} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Search Operations</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.searchops} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Bytes Sent</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.bytessent} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>One Level Searches</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.onelevelsearchops} size="10" readOnly />
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Entries Sent</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.entriesreturned} size="10" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>Whole Tree Searches</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <input type="text"
value={this.props.data.wholesubtreesearchops} size="10" readOnly />
+ </Col>
+
+ </Row>
+ </div>
+ </div>
+ );
+ }
+}
+
+// Prop types and defaults
+
+SNMPMonitor.propTypes = {
+ data: PropTypes.object,
+ reload: PropTypes.func
+};
+
+SNMPMonitor.defaultProps = {
+ data: {},
+ reload: noop
+};
+
+export default SNMPMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/suffixMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/suffixMonitor.jsx
new file mode 100644
index 0000000..f859f57
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/monitor/suffixMonitor.jsx
@@ -0,0 +1,423 @@
+import React from "react";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import {
+ DonutChart,
+ PieChart,
+ ControlLabel,
+ Row,
+ Col,
+ Icon,
+ Nav,
+ NavItem,
+ TabContent,
+ TabPane,
+ TabContainer,
+ noop
+} from "patternfly-react";
+import d3 from "d3";
+
+export class SuffixMonitor extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = {
+ activeKey: 1,
+ };
+ this.handleNavSelect = this.handleNavSelect.bind(this);
+ }
+
+ handleNavSelect(key) {
+ this.setState({ activeKey: key });
+ }
+
+ render() {
+ let badColor = "#d01c8b";
+ let warningColor = "#ffc107";
+ let goodColor = "#4dac26";
+ let emptyColor = "#d3d3d3";
+ let donutColorEC = goodColor;
+ let donutColorECMiss = emptyColor;
+ let donutColorECUtil = goodColor;
+ let donutColorDN = goodColor;
+ let donutColorDNMiss = emptyColor;
+ let donutColorDNUtil = goodColor;
+
+ // Entry cache
+ let cachehit = parseInt(this.props.data.entrycachehitratio[0]);
+ let cachemax = parseInt(this.props.data.maxentrycachesize[0]);
+ let cachecurr = parseInt(this.props.data.currententrycachesize[0]);
+ let cachecount = parseInt(this.props.data.currententrycachecount[0]);
+ let utilratio = Math.round((cachecurr / cachemax) * 100);
+ // DN cache
+ let dncachehit = parseInt(this.props.data.dncachehitratio[0]);
+ let dncachemax = parseInt(this.props.data.maxdncachesize[0]);
+ let dncachecurr = parseInt(this.props.data.currentdncachesize[0]);
+ let dncachecount = parseInt(this.props.data.currentdncachecount[0]);
+ let dnutilratio = Math.round((dncachecurr / dncachemax) * 100);
+
+ // Adjust ratios if needed
+ if (utilratio == 0) {
+ utilratio = 1;
+ }
+ if (dnutilratio == 0) {
+ dnutilratio = 1;
+ }
+
+ // Entry Cache
+ if (cachehit > 89) {
+ donutColorEC = goodColor;
+ } else if (cachehit > 74) {
+ donutColorEC = warningColor;
+ } else {
+ if (cachehit < 50) {
+ // Pie chart shows higher catagory, so we need to highlight the misses
+ donutColorECMiss = badColor;
+ } else {
+ donutColorEC = badColor;
+ }
+ }
+ // Entry cache utilization
+ if (cachehit < 90) {
+ if (utilratio > 95) {
+ donutColorECUtil = badColor;
+ } else if (utilratio > 90) {
+ donutColorECUtil = warningColor;
+ }
+ }
+ // DN cache ratio
+ if (dncachehit > 89) {
+ donutColorDN = goodColor;
+ } else if (dncachehit > 74) {
+ donutColorDN = warningColor;
+ } else {
+ if (dncachehit < 50) {
+ // Pie chart shows higher catagory, so we need to highlight the misses
+ donutColorDNMiss = badColor;
+ } else {
+ donutColorDN = badColor;
+ }
+ }
+ // DN cache utilization
+ if (dncachehit < 90) {
+ if (dnutilratio > 95) {
+ donutColorDNUtil = badColor;
+ } else if (dnutilratio > 90) {
+ donutColorDNUtil = warningColor;
+ }
+ }
+
+ let suffixIcon = "tree";
+ if (this.props.dbtype == "subsuffix") {
+ suffixIcon = "leaf";
+ }
+
+ return (
+ <div id="monitor-suffix-page"
className="container-fluid">
+ <Row>
+ <Col sm={12} className="ds-word-wrap">
+ <ControlLabel className="ds-suffix-header">
+ <Icon type="fa" name={suffixIcon} />
{this.props.suffix} (<i>{this.props.bename}</i>)
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh suffix monitor"
+ onClick={() => this.props.reload(this.props.suffix)}
+ />
+ </ControlLabel>
+ </Col>
+ </Row>
+ <p />
+ <TabContainer id="basic-tabs-pf"
onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html: 'Entry
Cache'}} />
+ </NavItem>
+ <NavItem eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html: 'DN
Cache'}} />
+ </NavItem>
+ </Nav>
+ <TabContent>
+
+ <TabPane eventKey={1}>
+ <div className="ds-margin-top-lg">
+ <div className="ds-container">
+ <div className="ds-divider" />
+ <div className="ds-left-margin"
title="The entry cache hit ratio. If the chart is RED then the hit ratio is below
90% and might require further cache tuning">
+ <DonutChart
+ id="monitor-entry-cache-pie"
+ size={{width: 180, height: 120}}
+ data={{
+ columns: [['miss', 100 -
cachehit], ['hit', cachehit]],
+ colors: {
+ 'hit': donutColorEC,
+ 'miss':
donutColorECMiss,
+ },
+ order: null,
+ unload: true
+ }}
+ title={{type: 'percent'}}
+ legend={{show: true, position:
'right'}}
+ />
+ <b>Entry Cache Hit Ratio</b>
+ </div>
+ <div className="ds-chart-right"
title="How much of the allocated cache space is used (max size vs current size). If
the chart is RED then you should to increase the max cache size because the cache hit
ratio is below 90%">
+ <PieChart
+ id="monitor-entry-util-pie"
+ size={{width: 180, height: 120}}
+ data={{
+ columns: [
+ ['Used', utilratio],
+ ['Unused', 100 -
utilratio],
+ ],
+ colors: {
+ 'Used':
donutColorECUtil,
+ 'Unused': emptyColor,
+ },
+ order: null,
+ type: 'pie'
+ }}
+ pie={{
+ label: {
+ format: function (value, ratio,
id) {
+ return
d3.format(',%')(value / 100);
+ }
+ }
+ }}
+ title={{type: 'pie'}}
+ legend={{show: true, position:
'right'}}
+ />
+ <b>Entry Cache Utilization</b>
+ <div>
+ (Entries in cache:
<b>{cachecount}</b>)
+ </div>
+ </div>
+ </div>
+ <p />
+ <hr />
+ <div>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Entry Cache Hit Ratio
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.entrycachehitratio} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Entry Cache Tries
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.entrycachetries} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Entry Cache Hits
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.entrycachehits} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Entry Cache Max Size
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.maxentrycachesize} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Entry Cache Current Size
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.currententrycachesize} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Entry Cache Max Entries
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.maxentrycachecount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ Entry Cache Count
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.currententrycachecount} size="35" readOnly />
+ </Col>
+ </Row>
+ </div>
+ </div>
+ </TabPane>
+
+ <TabPane eventKey={2}>
+ <div className="ds-margin-top-lg">
+ <div className="ds-container">
+ <div className="ds-divider" />
+ <div className="ds-left-margin"
title="The DN cache hit ratio. If the chart is RED then the hit ratio is below 90%
and might require further cache tuning">
+ <DonutChart
+ id="monitor-entry-cache-pie"
+ size={{width: 180, height: 120}}
+ data={{
+ columns: [['miss', 100 -
dncachehit], ['hit', dncachehit]],
+ colors: {
+ 'hit': donutColorDN,
+ 'miss':
donutColorDNMiss,
+ },
+ order: null,
+ }}
+ title={{type: 'percent'}}
+ legend={{show: true, position:
'right'}}
+ />
+ <b
className="ds-left-margin">DN Cache Hit Ratio</b>
+ </div>
+ <div className="ds-chart-right"
title="How much of the allocated cache space is used (max size vs current size). If
the chart is RED then you should to increase the max cache size because the cache hit
ratio is below 90%">
+ <PieChart
+ id="monitor-entry-util-pie"
+ size={{width: 180, height: 120}}
+ data={{
+ columns: [
+ ['Used', dnutilratio],
+ ['Unused', 100 -
dnutilratio],
+ ],
+ colors: {
+ 'Used':
donutColorDNUtil,
+ 'Unused': emptyColor,
+ },
+ order: null,
+ type: 'pie'
+ }}
+ pie={{
+ label: {
+ format: function (value, ratio,
id) {
+ return
d3.format(',%')(value / 100);
+ }
+ }
+ }}
+ title={{type: 'pie'}}
+ legend={{show: true, position:
'right'}}
+ />
+ <div
className="ds-left-margin">
+ <b>DN Cache Utilization</b>
+ <div>
+ (DN's in cache:
<b>{dncachecount}</b>)
+ </div>
+ </div>
+ </div>
+ </div>
+ <hr />
+ <div>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ DN Cache Hit Ratio
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.dncachehitratio} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ DN Cache Tries
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.dncachetries} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ DN Cache Hits
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.dncachehits} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ DN Cache Max Size
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.maxdncachesize} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ DN Cache Current Size
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.currentdncachesize} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ DN Cache Max Count
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.maxdncachecount} size="35" readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={3}>
+ <ControlLabel>
+ DN Cache Current Count
+ </ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <input type="text"
value={this.props.data.currentdncachecount} size="35" readOnly />
+ </Col>
+ </Row>
+ </div>
+ </div>
+ </TabPane>
+ </TabContent>
+ </div>
+ </TabContainer>
+ </div>
+ );
+ }
+}
+
+SuffixMonitor.propTypes = {
+ suffix: PropTypes.string,
+ data: PropTypes.object,
+ bename: PropTypes.string,
+ reload: PropTypes.func,
+};
+
+SuffixMonitor.defaultProps = {
+ suffix: "",
+ data: {},
+ bename: "",
+ reload: noop,
+};
+
+export default SuffixMonitor;
diff --git a/src/cockpit/389-console/src/lib/notifications.jsx
b/src/cockpit/389-console/src/lib/notifications.jsx
index a2b23e5..ad0b87f 100644
--- a/src/cockpit/389-console/src/lib/notifications.jsx
+++ b/src/cockpit/389-console/src/lib/notifications.jsx
@@ -25,7 +25,7 @@ class NotificationController extends React.Component {
<strong>{notification.header}</strong>
)}
{notification.type == "error" ? (
- <pre>{notification.message}</pre>
+ <pre
className="ds-width-auto">{notification.message}</pre>
) : (
<span>{notification.message}</span>
)}
diff --git a/src/cockpit/389-console/src/lib/tools.jsx
b/src/cockpit/389-console/src/lib/tools.jsx
index 442d032..5d482ba 100644
--- a/src/cockpit/389-console/src/lib/tools.jsx
+++ b/src/cockpit/389-console/src/lib/tools.jsx
@@ -56,3 +56,50 @@ export function log_cmd(js_func, desc, cmd_array) {
);
}
}
+
+// Convert DS timestamp to a friendly string: 20180921142257Z -> 10/21/2018, 2:22:57
PM
+export function get_date_string (timestamp) {
+ 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();
+}
+
+// Take two directory server tiemstamps and get the elapsed time
+export function get_date_diff(start, end) {
+ // Get the server's 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((currDate - (startDate)) / 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`;
+}
diff --git a/src/cockpit/389-console/src/monitor.html
b/src/cockpit/389-console/src/monitor.html
deleted file mode 100644
index ba35e5c..0000000
--- a/src/cockpit/389-console/src/monitor.html
+++ /dev/null
@@ -1,916 +0,0 @@
-
- <div id="monitor-server-page" class="all-pages" hidden>
- <h3 class="ds-config-header">Server Information</h3>
- <div class="ds-container">
- <div class="ds-inline">
- <div>
- <label for="monitor-serverid"
class="ds-label-xsm">Server Instance</label><input
type="text"
- class="ds-ro-input" id="monitor-serverid"
value="slapd-localhost" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-status"
class="ds-label-xsm">Server Status</label><input
type="text"
- class="ds-ro-input" id="monitor-status"
value="Started" size="30" readOnly/>
- </div>
- </div>
- <div class="ds-divider"></div>
- <div class="ds-inline">
- <div>
- <label for="monitor-version"
class="ds-label-xsm">Version</label><input type="text"
- class="ds-ro-input" id="monitor-version"
value="1.4.0.5.20180220gitfeb11dc33" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-build" class="ds-label-xsm">Build
Number</label><input type="text"
- class="ds-ro-input" id="monitor-build"
value="B2018.051.1911" size="30" readOnly/>
- </div>
- </div>
- </div>
- <hr>
- <h3 class="ds-config-header">Server Statistics</h3>
- <div class="ds-container">
- <div class="ds-inline">
- <div>
- <label for="monitor-server-dtablesize"
class="ds-monitor-label">Max File Descriptors</label><input
type="text"
- class="ds-ro-input" id="monitor-server-dtablesize"
value="1024" size="12" readOnly/>
- </div>
- <div>
- <label for="monitor-server-threads"
class="ds-monitor-label">Threads</label><input
type="text"
- class="ds-ro-input" id="monitor-server-threads"
value="24" size="12" readOnly/>
- </div>
- <div>
- <label for="monitor-server-totalconnections"
class="ds-monitor-label">Total Connections</label><input
type="text"
- class="ds-ro-input" id="monitor-server-totalconnections"
value="101" size="12" readOnly/>
- </div>
- <div>
- <label for="monitor-server-currentconnections"
class="ds-monitor-label">Current Conections</label><input
type="text"
- class="ds-ro-input"
id="monitor-server-currentconnections" value="4" size="12"
readOnly/>
- </div>
- <div>
- <label for="monitor-server-currentconnectionsatmaxthreads"
class="ds-monitor-label">Conns At Max Threads</label><input
type="text"
- class="ds-ro-input"
id="monitor-server-currentconnectionsatmaxthreads" value="0"
size="12" readOnly/>
- </div>
- <div>
- <label for="monitor-server-maxthreadsperconnhits"
class="ds-monitor-label">Conns Hit Max Threads</label><input
type="text"
- class="ds-ro-input"
id="monitor-server-maxthreadsperconnhits" value="0"
size="12" readOnly/>
- </div>
- <div>
- <label for="monitor-server-readwaiters"
class="ds-monitor-label">Threads Waiting To Read</label><input
type="text"
- class="ds-ro-input" id="monitor-server-readwaiters"
value="0" size="12" readOnly/>
- </div>
- </div>
- <div class="ds-divider"></div>
- <div class="ds-inline">
- <div>
- <label for="monitor-server-opsinitiated"
class="ds-monitor-label-med">Operations Started</label><input
type="text"
- class="ds-ro-input" id="monitor-server-opsinitiated"
value="0" size="12" readOnly/>
- </div>
- <div>
- <label for="monitor-server-opscompleted"
class="ds-monitor-label-med">Operations Completed</label><input
type="text"
- class="ds-ro-input" id="monitor-server-opscompleted"
value="0" size="12" readOnly/>
- </div>
- <div>
- <label for="monitor-server-entriessent"
class="ds-monitor-label-med">Entries Returned To
Clients</label><input type="text"
- class="ds-ro-input" id="monitor-server-entriessent"
value="0" size="12" readOnly/>
- </div>
- <div>
- <label for="monitor-server-bytessent"
class="ds-monitor-label-med">Bytes Sent to Clients</label><input
type="text"
- class="ds-ro-input" id="monitor-server-bytessent"
value="0" size="12" readOnly/>
- </div>
- <div>
- <label for="monitor-server-starttime"
class="ds-monitor-label-med">Server Started</label><input
type="text"
- class="ds-ro-input" id="monitor-server-starttime"
value="0" size="12" readOnly/>
- </div>
- <div>
- <label for="monitor-server-uptime"
class="ds-monitor-label-med">Server Uptime</label><input
type="text"
- class="ds-ro-input" id="monitor-server-uptime"
value="0" size="12" readOnly/>
- </div>
- </div>
- </div>
- <div>
- <hr>
- <h3 class="ds-config-header">Connection Table</h3>
- <table id="monitor-conn-table" class="display ds-table"
cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>Connection Opened</th>
- <th>IP Address</th>
- <th>Connection ID</th>
- <th>Operations Started</th>
- <th>Operations Completed</th>
- <th>Bind DN</th>
- <th>Read/Write</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>2017023402340234Z</td>
- <td>127.0.0.1</td>
- <td>1</td>
- <td>10</td>
- <td>10</td>
- <td>cn=Directory Manager</td>
- <td>-</td>
- </tr>
- <tr>
- <td>2017023402340234Z</td>
- <td>::1</td>
- <td>11</td>
- <td>102</td>
- <td>101</td>
- <td>uid=mreynolds,ou=people,ou=redhat,dc=example,dc=com</td>
- <td>-</td>
- </tr>
- </tbody>
- </table>
- <p></p>
- </div>
- </div>
-
- <div id="monitor-db-page" class="all-pages" hidden>
- <h3 class="ds-config-header">Database Monitoring</h3>
- <div class="ds-container">
-
- <div id="monitor-db-tree" class="jstree-open
ds-monitor-tree">
- <ul>
- <li class="jstree-open ds-treenode"
data-jstree='{"icon":"glyphicon glyphicon-tasks",
"opened":true, "selected":true}'
id="monitor-db-main">Database Performance
- <ul>
- <li title="suffix"
id="monitor-suffix-dc=red,dc=hat,dc=com,dc=lab,dc=example,dc=com"
- data-jstree='{"icon":"glyphicon
glyphicon-tree-conifer"}'>dc=red,dc=hat,dc=com,dc=lab,dc=example,dc=com
- <ul>
- <li title="sub suffix"
id="monitor-suffix-ou=People,dc=example,dc=com"
data-jstree='{"icon":"glyphicon
glyphicon-leaf"}'>ou=People,dc=example,dc=com</li>
- </ul>
- </li>
- <li title="suffix" id="monitor-suffix-o=ipaca"
data-jstree='{"icon":"glyphicon
glyphicon-tree-conifer"}'>o=ipaca</li>
- </ul>
- </li>
- </ul>
- </div>
-
- <div class="ds-divider"></div>
-
- <div name="tree content">
- <div id="db-content">
- <h3 class="ds-config-header">Database Performance
Statistics</h3>
- <hr>
- <div class="ds-container">
- <div>
- <div class="pct-donut-chart-pf example-donut-chart-utilization
ds-container ds-chart-center">
- <div class="pct-donut-chart-pf-chart">
- <div title="The database cache hit ratio. If the chart is RED
then the hit ratio is below 90% and might require further cache tuning"
id="monitor-db-cache-hitratio-chart"></div>
- <span class="pct-donut-chart-pf-label ds-center">
- <b>DB Cache Hit Ratio</b>
- </span>
- </div>
- </div>
-
- <hr class="ds-hr">
- <div class="ds-inline">
- <div>
- <label for="monitor-db-dbcachehitratio"
class="ds-monitor-label">Database Cache Hit Ratio</label><input
type="text"
- class="ds-input" id="monitor-db-dbcachehitratio"
value="99%" size="20" readOnly/>
- </div>
- <div>
- <label for="monitor-db-dbcachetries"
class="ds-monitor-label">Database Cache Tries</label><input
type="text"
- class="ds-input" id="monitor-db-dbcachetries"
value="19541" size="20" readOnly/>
- </div>
- <div>
- <label for="monitor-db-dbcachehits"
class="ds-monitor-label">Database Cache Hits</label><input
type="text"
- class="ds-input" id="monitor-db-dbcachehits"
value="19507" size="20" readOnly/>
- </div>
- <div>
- <label for="monitor-db-dbcachepagein"
class="ds-monitor-label">Cache Pages Read</label><input
type="text"
- class="ds-input" id="monitor-db-dbcachepagein"
value="34" size="20" readOnly/>
- </div>
- <div>
- <label for="monitor-db-dbcachepageout"
class="ds-monitor-label">Cache Pages Written</label><input
type="text"
- class="ds-input" id="monitor-db-dbcachepageout"
value="35" size="20" readOnly/>
- </div>
- <div>
- <label for="monitor-db-dbcacheroevict"
class="ds-monitor-label">Read-Only Page Evictions</label><input
type="text"
- class="ds-input" id="monitor-db-dbcacheroevict"
value="0" size="20" readOnly/>
- </div>
- <div>
- <label for="monitor-db-dbcacherwevict"
class="ds-monitor-label">Read-Write Page Evictions</label><input
type="text"
- class="ds-input" id="monitor-db-dbcacherwevict"
value="0" size="20" readOnly/>
- </div>
- </div>
- </div>
-
- <div class="ds-divider"></div>
- <div class="ds-divider"></div>
-
- <div>
- <div class="pct-donut-chart-pf example-donut-chart-utilization
ds-container">
- <div class="pct-donut-chart-pf-chart ds-chart-left">
- <div title="The NDN cache hit ratio. If the chart is RED then
the hit ratio is below 90% and might require further cache tuning"
id="monitor-ndn-cache-hitratio-chart"></div>
- <span class="pct-donut-chart-pf-label">
- <b>NDN Cache Hit Ratio</b>
- </span>
- </div>
- <div class="pct-donut-chart-pf-chart ds-chart-right">
- <div title="How much of the allocated cache space is used (max
size vs current size). If the chart is RED then you should to increase the max cache size
because the cache hit ratio is below 90%"
- id="monitor-ndn-cache-util-chart"></div>
- <span class="pct-donut-chart-pf-label">
- <b>NDN Cache Utilization</b>
- </span>
- </div>
- </div>
- <hr class="ds-hr">
- <div class="ds-inline">
- <div>
- <label title ="Normalized DN Cache hit ratio"
for="monitor-db-normalizeddncachehitratio"
class="ds-monitor-label">NDN Cache Hit Ratio</label><input
type="text"
- class="ds-input"
id="monitor-db-normalizeddncachehitratio" value="83%"
size="30" readOnly/>
- </div>
- <div>
- <label title ="Normalized DN Cache tries"
for="monitor-db-normalizeddncachetries"
class="ds-monitor-label">NDN Cache Tries</label><input
type="text"
- class="ds-input"
id="monitor-db-normalizeddncachetries" value="7680"
size="30" readOnly/>
- </div>
- <div>
- <label title ="Normalized DN Cache hits"
for="monitor-db-normalizeddncachehits" class="ds-monitor-label">NDN
Cache Hits</label><input type="text"
- class="ds-input"
id="monitor-db-normalizeddncachehits" value="6302" size="30"
readOnly/>
- </div>
- <div>
- <label title ="Normalized DN Cache evictions"
for="monitor-db-normalizeddncacheevictions"
class="ds-monitor-label">NDN Cache Evictions</label><input
type="text"
- class="ds-input"
id="monitor-db-normalizeddncacheevictions" value="0"
size="30" readOnly/>
- </div>
- <div>
- <label title ="Normalized DN Cache max size in bytes"
for="monitor-db-maxnormalizeddncachesize"
class="ds-monitor-label">NDN Cache Max Size</label><input
type="text"
- class="ds-input"
id="monitor-db-maxnormalizeddncachesize" value="25165824"
size="30" readOnly/>
- </div>
- <div>
- <label title ="Normalized DN Cache current size of the cache in
bytes" for="monitor-db-currentnormalizeddncachesize"
class="ds-monitor-label">NDN Current Cache Size</label><input
type="text"
- class="ds-input"
id="monitor-db-currentnormalizeddncachesize" value="19891200"
size="30" readOnly/>
- </div>
- <div>
- <label title ="Normalized DN Cache DN count"
for="monitor-db-currentnormalizeddncachecount"
class="ds-monitor-label">NDN Cache DN Count</label><input
type="text"
- class="ds-input"
id="monitor-db-currentnormalizeddncachecount" value="1276"
size="30" readOnly/>
- </div>
- <div>
- <label title ="Normalized DN Cache thread size in bytes"
for="monitor-db-normalizeddncachethreadsize"
class="ds-monitor-label">NDN Cache Thread Size</label><input
type="text"
- class="ds-input"
id="monitor-db-normalizedndncachethreadsize" value="1048576"
size="30" readOnly/>
- </div>
- <div>
- <label title ="Normalized DN Cache thread slot size"
for="monitor-db-normalizeddnslots" class="ds-monitor-label">NDN
Cache Thread Slots</label><input type="text"
- class="ds-input"
id="monitor-db-normalizeddnslots" value="8192" size="30"
readOnly/>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <!-- Suffix page -->
- <div id="monitor-suffix-page" class="ds-tree-content"
hidden>
- <h3 class="ds-config-header"
id="monitor-suffix-header"></h3>
- <hr>
- <div class="ds-container">
- <div class="">
- <!-- <div id="monitor-entry-cache-chart"
class="pie-chart-pf example-pie-chart-mini"></div> -->
- <div class="pct-donut-chart-pf example-donut-chart-utilization
ds-container">
- <div class="pct-donut-chart-pf-chart ds-chart-left">
- <div title="The entry cache hit ratio. If the chart is RED
then the hit ratio is below 90% and might require further cache tuning"
id="monitor-entry-cache-hitratio-chart"></div>
- <span class="pct-donut-chart-pf-label">
- <b>Entry Cache Hit Ratio</b>
- </span>
- </div>
- <div class="pct-donut-chart-pf-chart ds-chart-right">
- <div title="How much of the allocated cache space is used (max
size vs current size). If the chart is RED then you should to increase the max cache size
because the cache hit ratio is below 90%"
- id="monitor-entry-cache-util-chart"></div>
- <span class="pct-donut-chart-pf-label">
- <b>Entry Cache Utilization</b>
- </span>
- </div>
- </div>
- <p></p>
- <hr class="ds-hr">
- <div class="ds-inline">
- <div>
- <label for="monitor-be-entrycachehitratio"
class="ds-label-med">Entry Cache Hit Ratio</label><input
type="text"
- class="ds-ro-input"
id="monitor-be-entrycachehitratio" value="99%" size="30"
readOnly/>
- </div>
- <div>
- <label for="monitor-be-entrycachetries"
class="ds-label-med">Entry Cache Tries</label><input
type="text"
- class="ds-ro-input"
id="monitor-be-entrycachetries" value="3058" size="30"
readOnly/>
- </div>
- <div>
- <label for="monitor-be-entrycachehits"
class="ds-label-med">Database Cache Hits</label><input
type="text"
- class="ds-ro-input"
id="monitor-be-entrycachehits" value="3001" size="30"
readOnly/>
- </div>
- <div>
- <label for="monitor-be-maxentrycachesize"
class="ds-label-med">Entry Cache Max Size</label><input
type="text"
- class="ds-ro-input"
id="monitor-be-maxentrycachesize" value="512000" size="30"
readOnly/>
- </div>
- <div>
- <label for="monitor-be-currententrycachesize"
class="ds-label-med">Entry Cache Current Size</label><input
type="text"
- class="ds-ro-input"
id="monitor-be-currententrycachesize" value="389000"
size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-be-maxentrycachecount"
class="ds-label-med">Entry Cache Max Entries</label><input
type="text"
- class="ds-ro-input"
id="monitor-be-maxentrycachecount" value="-1" size="30"
readOnly/>
- </div>
- <div>
- <label for="monitor-be-currententrycachecount"
class="ds-label-med">Entry Cache Count</label><input
type="text"
- class="ds-ro-input"
id="monitor-be-currententrycachecount" value="105" size="30"
readOnly/>
- </div>
- </div>
- </div>
- <div class="ds-divider"></div>
- <div class="ds-divider"></div>
- <div>
- <div class="pct-donut-chart-pf example-donut-chart-utilization
ds-container">
- <div class="pct-donut-chart-pf-chart ds-chart-left">
- <div title="The entry cache hit ratio"
id="monitor-dn-cache-hitratio-chart"></div>
- <span class="pct-donut-chart-pf-label">
- <b>DN Cache Hit Ratio</b>
- </span>
- </div>
- <div class="pct-donut-chart-pf-chart ds-chart-right">
- <div title="How much of the allocated cache space is used (max
size vs current size). If the chart is RED then you should to increase the max cache size
because the cache hit ratio is below 90%"
-
- id="monitor-dn-cache-util-chart"></div>
- <span class="pct-donut-chart-pf-label">
- <b>DN Cache Utilization</b>
- </span>
- </div>
- </div>
- <p></p>
- <hr class="ds-hr">
- <div class="ds-inline">
- <div>
- <label title =""
for="monitor-be-dncachehitratio" class="ds-label-med">DN Cache Hit
Ratio</label><input type="text"
- class="ds-ro-input"
id="monitor-be-dncachehitratio" value="100%" size="30"
readOnly/>
- </div>
- <div>
- <label title ="" for="monitor-be-dncachetries"
class="ds-label-med">DN Cache Tries</label><input
type="text"
- class="ds-ro-input" id="monitor-be-dncachetries"
value="105" size="30" readOnly/>
- </div>
- <div>
- <label title ="" for="monitor-be-dncachehits"
class="ds-label-med">DN Cache Hits</label><input
type="text"
- class="ds-ro-input" id="monitor-be-dncachehits"
value="105" size="30" readOnly/>
- </div>
- <div>
- <label title ="" for="monitor-be-maxdncachesize"
class="ds-label-med">DN Cache Max Size</label><input
type="text"
- class="ds-ro-input"
id="monitor-be-maxdncachesize" value="16777216" size="30"
readOnly/>
- </div>
- <div>
- <label title =""
for="monitor-be-currentdncachesize" class="ds-label-med">DN Cache
Current Size</label><input type="text"
- class="ds-ro-input"
id="monitor-be-currentdncachesize" value="16809" size="30"
readOnly/>
- </div>
- <div>
- <label title =""
for="monitor-be-maxdncachecount" class="ds-label-med">DN Cache Max
Count</label><input type="text"
- class="ds-ro-input"
id="monitor-be-maxdncachecount" value="-1" size="30"
readOnly/>
- </div>
- <div>
- <label title =""
for="monitor-be-currentdncachecount" class="ds-label-med">DN Cache
Current Count</label><input type="text"
- class="ds-ro-input"
id="monitor-be-currentdncachecount" value="105" size="30"
readOnly/>
- </div>
- </div>
- </div>
- </div>
-
- <div>
- <hr>
- <h3 class="ds-config-header">Index Statistics</h3>
- <hr class="ds-hr">
- <table id="monitor-index-table" class="display
ds-table" cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>Attribute</th>
- <th>Cache Hits</th>
- <th>Cache Misses</th>
- <th>Pages Read</th>
- <th>Pages Written</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>uid</td>
- <td>101</td>
- <td>10</td>
- <td>1</td>
- <td>2</td>
- </tr>
- <tr>
- <td>cn</td>
- <td>705</td>
- <td>3</td>
- <td>2</td>
- <td>2</td>
- </tr>
- </tbody>
- </table>
- <p></p>
- </div>
- </div>
- </div>
-
- </div>
- </div>
-
- <div id="monitor-snmp-page" class="all-pages" hidden>
- <h3 class="ds-config-header">SNMP Counters</h3>
- <hr>
- <div class="ds-container">
- <div class="ds-inline">
- <div>
- <label for="monitor-snmp-anonymousbinds"
class="ds-label-med">Anonymous Binds</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-anonymousbinds"
value="5" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-unauthbinds"
class="ds-label-med">Unauthenticated Binds</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-unauthbinds"
value="5" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-simpleauthbinds"
class="ds-label-med">Simple Auth Binds</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-simpleauthbinds"
value="77" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-strongauthbinds"
class="ds-label-med">Strong Auth Binds</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-strongauthbinds"
value="0" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-inops"
class="ds-label-med">Initiated Operations</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-inops"
value="818" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-compareops"
class="ds-label-med">Compare Operations</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-compareops"
value="1" size="30" readOnly/>
- </div>
- <div>
- <label for="mnitor-snmp-addentryops"
class="ds-label-med">Add Operations</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-addentryops"
value="5" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-removeentryops"
class="ds-label-med">Delete Operations</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-removeentryops"
value="50" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-modifyentryops"
class="ds-label-med">Modify Operations</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-modifyentryops"
value="501" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-modifyrdnops"
class="ds-label-med">ModRDN Operations</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-modifyrdnops"
value="0" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-searchops"
class="ds-label-med">Search Operations</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-searchops"
value="5" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-onelevelsearchops"
class="ds-label-med">One Level Searches</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-onelevelsearchops"
value="2" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-wholesubtreesearchops"
class="ds-label-med">Whole Tree Searches</label><input
type="text"
- class="ds-ro-input"
id="monitor-snmp-wholesubtreesearchops" value="5" size="30"
readOnly/>
- </div>
- </div>
- <div class="ds-divider"></div>
- <div class="ds-divider"></div>
- <div class="ds-inline">
- <div>
- <label for="monitor-snmp-referrals"
class="ds-label-med">Referrals</label><input type="text"
- class="ds-ro-input" id="monitor-snmp-referrals"
value="5" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-referralsreturned"
class="ds-label-med">Returned Referrals</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-referralsreturned"
value="5" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-bindsecurityerrors"
class="ds-label-med">Bind Security Errors</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-bindsecurityerrors"
value="0" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-securityerrors"
class="ds-label-med">Security Errors</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-securityerrors"
value="0" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-errors"
class="ds-label-med">Errors</label><input type="text"
- class="ds-ro-input" id="monitor-snmp-errors"
value="5" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-connections"
class="ds-label-med">Current Connections</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-connections"
value="5" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-connectionseq"
class="ds-label-med">Total Connections</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-connectionseq"
value="50001" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-connectionsinmaxthreads"
class="ds-label-med">Conns in Max Threads</label><input
type="text"
- class="ds-ro-input"
id="monitor-snmp-connectionsinmaxthreads" value="0"
size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-connectionsmaxthreadscount"
class="ds-label-med">Conns Hit Max Threads</label><input
type="text"
- class="ds-ro-input"
id="monitor-snmp-connectionsmaxthreadscount" value="5"
size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-bytesrecv"
class="ds-label-med">Bytes Received</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-bytesrecv"
value="4563" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-bytessent"
class="ds-label-med">Bytes Sent</label><input type="text"
- class="ds-ro-input" id="monitor-snmp-bytessent"
value="993068" size="30" readOnly/>
- </div>
- <div>
- <label for="monitor-snmp-entriesreturned"
class="ds-label-med">Entries Sent</label><input
type="text"
- class="ds-ro-input" id="monitor-snmp-entriesreturned"
value="5" size="30" readOnly/>
- </div>
- </div>
- </div>
- <p></p>
- </div>
-
- <div id="monitor-log-access-page" class="all-pages" hidden>
- <h3>Access Log</h3>
- <hr>
- <label for="accesslog-lines"> Log Lines To
Show</label><select
- class="btn btn-default dropdown ds-log-dropdown"
id="accesslog-lines">
- <option>50</option>
- <option>100</option>
- <option>200</option>
- <option>300</option>
- <option>400</option>
- <option>500</option>
- <option>1000</option>
- <option>2000</option>
- <option>5000</option>
- <option>10000</option>
- </select><button id="accesslog-refresh-btn"
class="ds-adj-btn">Refresh</button><label
- class="ds-left-margin">Continuously Refresh<input
type="checkbox" class="ds-sm-left-margin"
id="accesslog-cont-refresh"></label>
- <textarea id="accesslog-area" class="ds-logarea"
readonly></textarea>
- <p></p>
- </div>
-
- <div id="monitor-log-audit-page" class="all-pages" hidden>
- <h3>Audit Log</h3>
- <hr>
- <label for="auditlog-lines"> Log Lines To
Show</label><select
- class="btn btn-default dropdown ds-log-dropdown"
id="auditlog-lines">
- <option>50</option>
- <option>100</option>
- <option>200</option>
- <option>300</option>
- <option>400</option>
- <option>500</option>
- <option>1000</option>
- <option>2000</option>
- <option>5000</option>
- <option>10000</option>
- </select><button id="auditlog-refresh-btn"
class="ds-adj-btn">Refresh</button><label
- class="ds-left-margin">Continuously Refresh<input
type="checkbox" class="ds-sm-left-margin"
id="auditlog-cont-refresh"></label>
- <textarea id="auditlog-area" class="ds-logarea"
readonly></textarea>
- <p></p>
- </div>
-
- <div id="monitor-log-auditfail-page" class="all-pages"
hidden>
- <h3>Audit Failure Log</h3>
- <hr>
- <label for="auditfaillog-lines"> Log Lines To
Show</label><select
- class="btn btn-default dropdown ds-log-dropdown"
id="auditfaillog-lines">
- <option>50</option>
- <option>100</option>
- <option>200</option>
- <option>300</option>
- <option>400</option>
- <option>500</option>
- <option>1000</option>
- <option>2000</option>
- <option>5000</option>
- <option>10000</option>
- </select><button id="auditfaillog-refresh-btn"
class="ds-adj-btn">Refresh</button><label
- class="ds-left-margin">Continuously Refresh<input
type="checkbox" class="ds-sm-left-margin"
id="auditfaillog-cont-refresh"></label>
- <textarea id="auditfaillog-area" class="ds-logarea"
readonly></textarea>
- <p></p>
- </div>
-
- <div id="monitor-log-errors-page" class="all-pages" hidden>
- <h3>Errors Log</h3>
- <hr>
- <label for="errorslog-lines"> Log Lines To
Show</label><select
- class="btn btn-default dropdown ds-left-margin"
id="errorslog-lines">
- <option>50</option>
- <option>100</option>
- <option>200</option>
- <option>300</option>
- <option>400</option>
- <option>500</option>
- <option>1000</option>
- <option>2000</option>
- <option>5000</option>
- <option>10000</option>
- </select><button id="errorslog-refresh-btn"
class="ds-adj-btn">Refresh</button><label
- class="ds-left-margin">Continuously Refresh<input
type="checkbox" class="ds-sm-left-margin"
id="errorslog-cont-refresh"></label><div
- class="dropdown ds-float-right"><label
- for="errorslog-sev-level">Filter Logging By Severity
Level</label><select
- class="btn btn-default dropdown ds-left-margin"
id="errorslog-sev-level">
- <option>Everything</option>
- <option>Error Messages</option>
- <option>Info Messages</option>
- <option disabled>──────────</option>
- <option>Emergency</option>
- <option>Alert</option>
- <option>Critical</option>
- <option>Error</option>
- <option>Warning</option>
- <option>Notice</option>
- <option>Info</option>
- <option>Debug</option>
- </select>
- </div>
- <textarea id="errorslog-area" class="ds-logarea"
readonly></textarea>
- <p></p>
- </div>
-
-
-
- <div id="monitor-repl-page" class="all-pages" hidden>
- <h3>Replication Monitoring for <select class="btn btn-default
dropdown" id="monitor-repl-backend-list">
- <option>dc=example,dc=com</option></select></h3>
- <hr>
- <h4>Replication Agreements</h4>
- <table id="monitor-repl-table" class="display ds-table"
cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>Agreement</th>
- <th>Replica</th>
- <th>Enabled</th>
- <th>Replication Lag</th>
- <th>Replication State</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>me2localhost</td>
- <td>localhost.localdomain:5555</td>
- <td>Yes</td>
- <td>00:00:00</td>
- <td data-sort="good"><div
class="ds-repl-state-good"><span class="glyphicon
glyphicon-thumbs-up"></span></div></td>
- <td><button class="btn btn-default ds-agmt-dropdown-button
repl-detail-btn" data-toggle="modal"
data-target="#monitor-agmt-form" type="button">View
Details</button></td>
- </tr>
- <tr>
- <td>me2beaker</td>
- <td>lab1.localdomain.com:389</td>
- <td>Yes</td>
- <td >00:00:02</td>
- <td data-sort="good"><div
class="ds-repl-state-good"><span class="glyphicon
glyphicon-thumbs-up"></span></div></td>
- <td><button class="btn btn-default ds-agmt-dropdown-button
repl-detail-btn" data-toggle="modal"
data-target="#monitor-agmt-form" type="button">View
Details</button></td>
- </tr>
- <tr>
- <td>me2fedora</td>
- <td>lab45.localdomain.com:389</td>
- <td>Yes</td>
- <td>00:09:02</td>
- <td data-sort="good"><div
class="ds-repl-state-good"><span class="glyphicon
glyphicon-thumbs-up"></span></div></td>
- <td><button class="btn btn-default ds-agmt-dropdown-button
repl-detail-btn" data-toggle="modal"
data-target="#monitor-agmt-form" type="button">View
Details</button></td>
- </tr>
- <tr>
- <td>me2beaker</td>
- <td>lab11.localdomain.com:389</td>
- <td>Yes</td>
- <td class="ds-repl-lag-bad">00:45:02</td>
- <td data-sort="bad"><div
class="ds-repl-state-bad"><span class="glyphicon
glyphicon-thumbs-down"></span></div></td>
- <td><button class="btn btn-default ds-agmt-dropdown-button
repl-detail-btn" data-toggle="modal"
data-target="#monitor-agmt-form" type="button">View
Details</button></td>
- </tr>
- </tbody>
- </table>
- <p></p>
- <hr>
- <h4>Windows Synchronization Agreements</h4>
- <table id="monitor-winsync-table" class="display ds-table"
cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>Agreement</th>
- <th>Windows Host/Port</th>
- <th>Windows Suffix</th>
- <th>Directory Server Suffix</th>
- <th>Replication State</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>my-winsync-agmt</td>
- <td>localhost.domain.com:389</td>
- <td>cn=users,dc=example,dc=com</td>
- <td>ou=people,dc=example,dc=com</td>
- <td data-sort="good"><div
class="ds-repl-state-good"><span class="glyphicon
glyphicon-thumbs-up"></span></div></td>
- <td><button class="btn btn-default ds-agmt-dropdown-button
repl-winsync-detail-btn" data-toggle="modal"
- data-target="#monitor-winsync-agmt-form"
type="button">View Details</button></td>
- </tr>
- </tbody>
- </table>
- <p></p>
- </div>
-
-
- <!-- Agmt detail modal -->
-
- <div class="modal fade" id="monitor-agmt-form"
data-backdrop="static" tabindex="-1" role="dialog"
aria-labelledby="repl-agmt-header" aria-hidden="true">
- <div class="modal-dialog ds-modal-wide">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close"
data-dismiss="modal" aria-hidden="true"
aria-label="Close">
- <span class="pficon pficon-close"></span>
- </button>
- <h4 class="modal-title"
id="repl-agmt-header">Replication Agreement Details</h4>
- </div>
- <div class="modal-body">
- <form class="form-horizontal">
- <div class="ds-container">
- <div class="ds-inline">
- <div>
- <label for="monitor-agmt-lag"
class="ds-label-med" title="The replication time lag between the supplier
and its consumer">Replication Lag</label><input
- class="ds-input" type="text"
id="monitor-agmt-lag" size="22" value="00:01" readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicaupdateinprogress"
class="ds-label-med" title="The replication time lag between the supplier
and its consumer">Replication In Progress</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicaupdateinprogress" size="22"
value="FALSE" readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5beginreplicarefresh"
class="ds-label-med" title="The replication time lag between the supplier
and its consumer">Replication Init In Progress</label><input
- class="ds-input" type="text"
id="monitor-nsds5beginreplicarefresh" size="22" value=""
readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicachangessentsincestartup"
class="ds-label-med" title="The replication time lag between the supplier
and its consumer">Changes Sent Since Startup</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicachangessentsincestartup" size="22"
value="1/0" readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicalastupdatestart"
class="ds-label-med" title="The time when the last update was sent to the
consumer">Last Update Started</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicalastupdatestart" size="22" value="Fri Mar
09 09:06:03 EST 2018" readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicalastupdateend"
class="ds-label-med" title="The the when the last updated was
finished">Last Update Ended</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicalastupdateend" size="22" value="Fri Mar
09 09:06:04 EST 2018" readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicalastinitstart"
class="ds-label-med" title="The the when the last updated was
finished">Last Init Started</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicalastinitstart" size="22" value="Fri Mar
09 09:06:04 EST 2018" readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicalastinitend"
class="ds-label-med" title="The the when the last updated was
finished">Last Init Ended</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicalastinitend" size="22" value="Fri Mar 09
09:06:04 EST 2018" readOnly/>
- </div>
- </div>
- <div class="ds-divider"></div>
- <div class="ds-inline">
- <div>
- <label for="monitor-nsds5replicahost"
class="ds-label-med" title="The replica/consumer host name">Replica
Host</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicahost" size="22"
value="localhost.localdomain" readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicaport"
class="ds-label-med" title="The replica/consumer port
number">Replica Port</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicaport" size="22" value="389"
readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicaroot"
class="ds-label-med" title="The suffix that is being
replicated">Replicated Suffix</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicaroot" size="22"
value="dc=example,dc=com" readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicatransportinfo"
class="ds-label-med" title="The replication agreement connection
protocol">Replication Conn Protocol</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicatransportinfo" size="22"
value="LDAPS" readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicabindmethod"
class="ds-label-med" title="The replication bind
method">Replication Bind Method</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicabindmethod" size="22" value="SIMPLE"
readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicabinddn"
class="ds-label-med" title="The replication bind
method">Replication Bind DN</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicabinddn" size="22" value="uid=replication
manager,cn=config" readOnly/>
- </div>
- <div>
- <label for="monitor-nsds5replicaenabled"
class="ds-label-med" title="The replication bind method">Agreement
State</label><input
- class="ds-input" type="text"
id="monitor-nsds5replicaenabled" size="22" value="Disabled"
readOnly/>
- </div>
- </div>
- </div>
- <div>
- <label for="monitor-nsds5replicalastinitstatus"
class="ds-label-med" title="The replication bind method">Last
Initialization Status</label>
- <textarea id="monitor-nsds5replicalastinitstatus"
rows="5" class="ds-agmt-textarea" readonly>Error (0) Total Update
Succeeded</textarea>
- <p></p>
- <label for="monitor-nsds5replicalastupdatestatus"
class="ds-label-med" title="The replication bind method">Last
Update Status</label>
- <textarea id="monitor-nsds5replicalastupdatestatus"
rows="5" class="ds-agmt-textarea" readonly>Error (0) Replica
acquired successfully: Incremental update succeeded</textarea>
- </div>
- </form>
- </div>
- <div class="modal-footer ds-modal-footer">
- <button type="button" class="btn btn-default
ds-button-left" id="monitor-test-winsync-repl"
data-dismiss="modal">Test Replication</button>
- <button type="button" class="btn btn-default
ds-button-right" data-dismiss="modal">Close</button>
- </div>
- </div>
- </div>
- </div>
-
-
- <!-- WINSYNC Agmt detail modal -->
-
- <div class="modal fade" id="monitor-winsync-agmt-form"
data-backdrop="static" tabindex="-1" role="dialog"
aria-labelledby="repl-winsync-agmt-header" aria-hidden="true">
- <div class="modal-dialog ds-modal-wide">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close"
data-dismiss="modal" aria-hidden="true"
aria-label="Close">
- <span class="pficon pficon-close"></span>
- </button>
- <h4 class="modal-title"
id="repl-winsync-agmt-header">Winsync Agreement Details</h4>
- </div>
- <div class="modal-body">
- <form class="form-horizontal">
- <div class="ds-container">
- <div class="ds-inline">
- <div>
- <label for="monitor-winsync-nsds5replicaupdateinprogress"
class="ds-label-med" title="The replication time lag between the supplier
and its consumer">Replication In Progress</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicaupdateinprogress" size="22"
value="FALSE" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds5beginreplicarefresh"
class="ds-label-med" title="The replication time lag between the supplier
and its consumer">Replication Init In Progress</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5beginreplicarefresh" size="22"
value="" readOnly/>
- </div>
- <div>
- <label
for="monitor-winsync-nsds5replicachangessentsincestartup"
class="ds-label-med" title="The replication time lag between the supplier
and its consumer">Changes Sent Since Startup</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicachangessentsincestartup" size="22"
value="1/0" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds5replicalastupdatestart"
class="ds-label-med" title="The time when the last update was sent to the
consumer">Last Update Started</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicalastupdatestart" size="22"
value="Fri Mar 09 09:06:03 EST 2018" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds5replicalastupdateend"
class="ds-label-med" title="The the when the last updated was
finished">Last Update Ended</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicalastupdateend" size="22"
value="Fri Mar 09 09:06:04 EST 2018" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds5replicalastinitstart"
class="ds-label-med" title="The the when the last updated was
finished">Last Init Started</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicalastinitstart" size="22"
value="Fri Mar 09 09:06:04 EST 2018" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds5replicalastinitend"
class="ds-label-med" title="The the when the last updated was
finished">Last Init Ended</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicalastinitend" size="22" value="Fri
Mar 09 09:06:04 EST 2018" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds5replicaenabled"
class="ds-label-med" title="The replication bind method">Agreement
State</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicaenabled" size="22"
value="Disabled" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds7newwinusersyncenabled"
class="ds-label-med" title="The the when the last updated was
finished">Sync New Users Enable</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds7newwinusersyncenabled" size="22"
value="on" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds7newwingroupsyncenabled"
class="ds-label-med" title="The the when the last updated was
finished">Sync New Groups Enabled</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds7newwingroupsyncenabled" size="22"
value="on" readOnly/>
- </div>
- </div>
- <div class="ds-divider"></div>
- <div class="ds-inline">
- <div>
- <label for="monitor-winsync-nsds5replicahost"
class="ds-label-med" title="The replica/consumer host name">Replica
Host</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicahost" size="22"
value="localhost.localdomain" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-winsync-nsds5replicaport"
class="ds-label-med" title="The replica/consumer port
number">Replica Port</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicaport" size="22" value="389"
readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds5replicaroot"
class="ds-label-med" title="The suffix that is being
replicated">Replicated Suffix</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicaroot" size="22"
value="dc=example,dc=com" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds5replicatransportinfo"
class="ds-label-med" title="The replication agreement connection
protocol">Replication Conn Protocol</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicatransportinfo" size="22"
value="LDAPS" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds5replicabindmethod"
class="ds-label-med" title="The replication bind
method">Replication Bind Method</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicabindmethod" size="22"
value="SIMPLE" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds5replicabinddn"
class="ds-label-med" title="The replication bind
method">Replication Bind DN</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds5replicabinddn" size="22"
value="uid=replication manager,cn=config" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds7windowsreplicasubtree"
class="ds-label-med" title="The replication bind method">Windows
Subtree</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds7windowsreplicasubtree" size="22"
value="cn=Users,dc=example,dc=com" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds7directoryreplicasubtree"
class="ds-label-med" title="The replication bind method">Directory
Server Subtree</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds7directoryreplicasubtree" size="22"
value="ou=People, dc=example,dc=com" readOnly/>
- </div>
- <div>
- <label for="monitor-winsync-nsds7windowsdomain"
class="ds-label-med" title="The replication bind method">Windows
Domain</label><input
- class="ds-input" type="text"
id="monitor-winsync-nsds7windowsdomain" size="22"
value="example.com" readOnly/>
- </div>
- </div>
- </div>
- <p></p>
- <div>
- <label for="monitor-winsync-nsds5replicalastinitstatus"
class="ds-label-med" title="The replication bind method">Last
Initialization Status</label>
- <textarea id="monitor-winsync-nsds5replicalastinitstatus"
rows="5" class="ds-agmt-textarea" readonly>Error (0) Total Update
Succeeded</textarea>
- <p></p>
- <label for="monitor-winsync-nsds5replicalastupdatestatus"
class="ds-label-med" title="The replication bind method">Last
Update Status</label>
- <textarea id="monitor-winsync-nsds5replicalastupdatestatus"
rows="5" class="ds-agmt-textarea" readonly>Error (0) Replica
acquired successfully: Incremental update succeeded</textarea>
- </div>
- </form>
- </div>
- <div class="modal-footer ds-modal-footer">
- <button type="button" class="btn btn-default
ds-button-left" id="monitor-test-winsync-repl"
data-dismiss="modal">Test Replication</button>
- <button type="button" class="btn btn-default
ds-button-right" data-dismiss="modal">Close</button>
- </div>
- </div>
- </div>
- </div>
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("<b>" + monitor_suffix
+ "</b>");
-
- // 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 = " <font size=\"2\"
color=\"red\"><b>(Agreement Disabled)</b></font>";
- }
- // clear_agmt_form();
-
- $("#monitor-agmt-header").html("<b>Replication Agreement
Details:</b> " + 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 = " <font size=\"2\"
color=\"red\"><b>(Agreement Disabled)</b></font>";
- }
- // clear_agmt_form();
-
- $("#repl-winsync-agmt-header").html("<b>Winsync Agreement
Details:</b> " + 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 =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4><b>Loading database monitor information
...</b></h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ monitor_element =
+ <DatabaseMonitor
+ data={this.state.ldbmData}
+ reload={this.reloadLDBM}
+ />;
+ }
+ } else if (this.state.node_name == "server-monitor") {
+ if (this.state.serverLoading) {
+ monitor_element =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4><b>Loading server monitor information
...</b></h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ monitor_element =
+ <ServerMonitor
+ data={this.state.serverData}
+ reload={this.reloadServer}
+ serverId={this.props.serverId}
+ />;
+ }
+ } else if (this.state.node_name == "snmp-monitor") {
+ if (this.state.snmpLoading) {
+ monitor_element =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4><b>Loading SNMP monitor information
...</b></h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ monitor_element =
+ <SNMPMonitor
+ data={this.state.snmpData}
+ reload={this.reloadSNMP}
+ />;
+ }
+ } else if (this.state.node_name == "access-log-monitor") {
+ monitor_element =
+ <AccessLogMonitor
+ data={this.state.accesslogData}
+ handleChange={this.handleAccessChange}
+ reload={this.refreshAccessLog}
+ reloading={this.state.accessReloading}
+ refreshing={this.state.accessRefreshing}
+ handleRefresh={this.accessRefreshCont}
+ lines={this.state.accessLines}
+ />;
+ } else if (this.state.node_name == "audit-log-monitor") {
+ monitor_element =
+ <AuditLogMonitor
+ data={this.state.auditlogData}
+ handleChange={this.handleAuditChange}
+ reload={this.refreshAuditLog}
+ reloading={this.state.auditReloading}
+ refreshing={this.state.auditRefreshing}
+ handleRefresh={this.auditRefreshCont}
+ lines={this.state.auditLines}
+ />;
+ } else if (this.state.node_name == "auditfail-log-monitor") {
+ monitor_element =
+ <AuditFailLogMonitor
+ data={this.state.auditfaillogData}
+ handleChange={this.handleAuditFailChange}
+ reload={this.refreshAuditFailLog}
+ reloading={this.state.auditfailReloading}
+ refreshing={this.state.auditfailRefreshing}
+ handleRefresh={this.auditFailRefreshCont}
+ lines={this.state.auditfailLines}
+ />;
+ } else if (this.state.node_name == "error-log-monitor") {
+ monitor_element =
+ <ErrorLogMonitor
+ data={this.state.errorlogData}
+ handleChange={this.handleErrorChange}
+ reload={this.refreshErrorLog}
+ reloading={this.state.errorReloading}
+ refreshing={this.state.errorRefreshing}
+ handleRefresh={this.errorRefreshCont}
+ handleSevLevel={this.handleSevChange}
+ lines={this.state.errorLines}
+ />;
+ } else if (this.state.node_name == "replication-monitor") {
+ if (this.state.replLoading) {
+ monitor_element =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Loading replication monitor information
...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ if (this.state.replicatedSuffixes.length < 1) {
+ monitor_element =
+ <div>
+ <p>There are no suffixes that have been configured
for replication</p>
+ </div>;
+ } else {
+ let suffixList = this.state.replicatedSuffixes.map((suffix)
=>
+ <option key={suffix}
value={suffix}>{suffix}</option>
+ );
+ monitor_element =
+ <div>
+ <Row>
+ <Col sm={12}>
+ <ControlLabel
className="ds-header">Replication Monitoring</ControlLabel>
+ <select className="ds-left-indent"
defaultValue={this.state.replSuffix} onChange={this.replSuffixChange}>
+ {suffixList}
+ </select>
+ <Icon className="ds-left-margin
ds-refresh"
+ type="fa" name="refresh"
title="Refresh replication monitor"
+ onClick={() =>
this.loadMonitorReplication()}
+ />
+ </Col>
+ </Row>
+ <div className="ds-margin-top-med">
+ <ReplMonitor
+ suffix={this.state.replSuffix}
+ serverId={this.props.serverId}
+ data={this.state[this.state.replSuffix]}
+ addNotification={this.addNotification}
+ reloadAgmts={this.reloadReplAgmts}
+ reloadWinsyncAgmts={this.reloadReplWinsyncAgmts}
+ key={this.state.replSuffix}
+ />
+ </div>
+ </div>;
+ }
+ }
+ } else if (this.state.node_name != "") {
+ // suffixes (example)
+ if (this.state.suffixLoading) {
+ monitor_element =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Loading suffix monitor information for
<b>{this.state.node_text} ...</b></h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else if (this.state.chainingLoading) {
+ monitor_element =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Loading chaining monitor information for
<b>{this.state.node_text} ...</b></h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ if (this.state.node_type == "dblink") {
+ monitor_element =
+ <ChainingMonitor
+ suffix={this.state.node_text}
+ bename={this.state.bename}
+ reload={this.loadMonitorChaining}
+ data={this.state[this.state.node_text].chainingData}
+ key={this.state.node_text}
+ />;
+ } else {
+ // Suffix
+ monitor_element =
+ <SuffixMonitor
+ suffix={this.state.node_text}
+ bename={this.state.bename}
+ reload={this.loadMonitorSuffix}
+ data={this.state[this.state.node_text].suffixData}
+ key={this.state.node_text}
+ />;
+ }
+ }
+ }
+ monitorPage =
+ <div className="container-fluid">
+ <NotificationController
+ notifications={this.state.notifications}
+ removeNotificationAction={this.removeNotification}
+ />
+ <div className="ds-container">
+ <div>
+ <div className="ds-tree">
+ <div className="tree-view-container"
id="db-tree"
+ style={treeViewContainerStyles}>
+ <TreeView
+ nodes={nodes}
+ highlightOnHover
+ highlightOnSelect
+ selectNode={this.selectNode}
+ />
+ </div>
+ </div>
+ </div>
+ <div className="ds-tree-content">
+ {monitor_element}
+ </div>
+ </div>
+ </div>;
+ } else {
+ monitorPage =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Loading monitor information ...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ }
+
+ return (
+ <div>
+ {monitorPage}
+ </div>
+ );
+ }
+}
+
+// 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
<div id="repl-winsync" class="all-pages" hidden>
<h3 class="ds-config-header">Windows Synchronization Agreements for
<select
class="btn btn-default dropdown"
id="select-repl-winsync-suffix">
- <option>dc=example,dc=com</option>
- <option>o=ipaca</option>
</select></h3>
<div class="ds-page-content">
<table id="repl-winsync-agmt-table" class="display
ds-repl-table" cellspacing="0" width="100%">
@@ -443,7 +441,7 @@ CleanAllRUV Tasks
</div>
</div>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="agmt-save">Save Agreement</button>
</div>
@@ -530,7 +528,7 @@ CleanAllRUV Tasks
</div>
</form>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="winsync-agmt-save">Save Agreement</button>
</div>
@@ -575,7 +573,7 @@ CleanAllRUV Tasks
</div>
</form>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="cleanallruv-save">Create Task</button>
</div>
@@ -620,7 +618,7 @@ CleanAllRUV Tasks
</div>
</form>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="add-repl-mgr-save">Add Manager</button>
</div>
@@ -645,7 +643,7 @@ CleanAllRUV Tasks
</select>
</form>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary"
id="select-attr-save">Add Attributes</button>
</div>
@@ -726,7 +724,7 @@ CleanAllRUV Tasks
</div>
</form>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="enable-repl-save">Save</button>
</div>
diff --git a/src/cockpit/389-console/src/replication.js
b/src/cockpit/389-console/src/replication.js
index 3aa3136..6ef3635 100644
--- a/src/cockpit/389-console/src/replication.js
+++ b/src/cockpit/389-console/src/replication.js
@@ -347,7 +347,7 @@ function get_and_set_repl_agmts () {
function get_and_set_cleanallruv() {
console.log("Loading replication tasks...");
- var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id +
'.socket','repl-tasks', 'list-cleanallruv'];
+ let cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id +
'.socket','repl-tasks', 'list-cleanruv-tasks'];
log_cmd('get_and_set_cleanallruv', 'Get the cleanAllRUV tasks', cmd);
cockpit.spawn(cmd, { superuser: true, "err": "message",
"environ": [ENV]}).done(function(data) {
var tasks = JSON.parse(data);
@@ -729,7 +729,7 @@ $(document).ready( function() {
$("#replication-content").show();
$("#repl-winsync").show();
});
- $("#repl-cleanallruv-btn").on("click", function() {
+ $("#repl-tasks-btn").on("click", function() {
$(".all-pages").hide();
$("#replication-content").show();
$("#repl-cleanallruv").show();
@@ -1975,7 +1975,7 @@ $(document).ready( function() {
}
log_cmd('#cleanallruv-save (click)', 'Create CleanAllRUV Task',
cmd);
cockpit.spawn(cmd, { superuser: true, "err": "message",
"environ": [ENV]}).done(function() {
- var list_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' +
server_id + '.socket','repl-tasks', 'list-cleanallruv'];
+ let list_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' +
server_id + '.socket','repl-tasks', 'list-cleanruv-tasks'];
log_cmd('#cleanallruv-save (click)', 'List all the CleanAllRUV
tasks', list_cmd);
cockpit.spawn(list_cmd, { superuser: true, "err": "message",
"environ": [ENV]}).done(function(data) {
repl_clean_table.clear().draw();
diff --git a/src/cockpit/389-console/src/schema.html
b/src/cockpit/389-console/src/schema.html
index 74388e2..b677d5b 100644
--- a/src/cockpit/389-console/src/schema.html
+++ b/src/cockpit/389-console/src/schema.html
@@ -155,7 +155,7 @@
<p><span class="spinner spinner-xs
spinner-inline"></span> Processing...<p>
</div>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="save-attr-button">Save</button>
</div>
@@ -245,7 +245,7 @@
<p><span class="spinner spinner-xs
spinner-inline"></span> Processing...<p>
</div>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="save-oc-button">Save</button>
</div>
diff --git a/src/cockpit/389-console/src/servers.html
b/src/cockpit/389-console/src/servers.html
index 7d3c745..8f40883 100644
--- a/src/cockpit/389-console/src/servers.html
+++ b/src/cockpit/389-console/src/servers.html
@@ -464,8 +464,6 @@
<h3 class="ds-config-header">Local Password Policies </h3>
<label class="ds-config-label-med"
for="local-pwp-suffix">Database Suffix</Label> <select
class="btn btn-default dropdown" id="local-pwp-suffix">
- <option>dc=example,dc=com</option>
- <option>o=ipaca</option>
</select>
<div class="ds-page-content">
<table id="passwd-policy-table" class="display
ds-repl-table" cellspacing="0" width="100%">
@@ -1054,7 +1052,7 @@
</div>
</form>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="sasl-map-save">Save</button>
</div>
@@ -1264,7 +1262,7 @@
</form>
</div>
</div>
- <div class="modal-footer ds-modal-footer">
+ <div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"
id="local-pwp-save">Save</button>
</div>
diff --git a/src/cockpit/389-console/webpack.config.js
b/src/cockpit/389-console/webpack.config.js
index 8c89041..d4ac364 100644
--- a/src/cockpit/389-console/webpack.config.js
+++ b/src/cockpit/389-console/webpack.config.js
@@ -30,8 +30,6 @@ var info = {
"fonts",
"images",
"index.html",
- "monitor.html",
- "monitor.js",
"replication.html",
"replication.js",
"rhds-banner.html",
diff --git a/src/lib389/cli/dsconf b/src/lib389/cli/dsconf
index b22736e..37e6282 100755
--- a/src/lib389/cli/dsconf
+++ b/src/lib389/cli/dsconf
@@ -15,6 +15,8 @@ import logging
import ldap
import sys
import signal
+import json
+import ast
from lib389 import DirSrv
from lib389._constants import DN_CONFIG, DN_DM
from lib389.cli_conf import config as cli_config
@@ -132,10 +134,15 @@ if __name__ == '__main__':
log.info("Command successful.")
except Exception as e:
log.debug(e, exc_info=True)
+ errmsg = str(e)
if args and args.json:
- sys.stderr.write(str(e) + '\n')
+ try:
+ msg = ast.literal_eval(errmsg)
+ except:
+ msg = {'desc': errmsg}
+ sys.stderr.write(json.dumps(msg))
else:
- log.error("Error: %s" % str(e))
+ log.error("Error: %s" % errmsg)
result = False
disconnect_instance(inst)
@@ -143,5 +150,3 @@ if __name__ == '__main__':
# Done!
if result is False:
sys.exit(1)
-
-
diff --git a/src/lib389/lib389/_mapped_object.py b/src/lib389/lib389/_mapped_object.py
index 6e2538e..a141877 100644
--- a/src/lib389/lib389/_mapped_object.py
+++ b/src/lib389/lib389/_mapped_object.py
@@ -491,6 +491,17 @@ class DSLdapObject(DSLogging):
entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE,
self._object_filter, attrlist=keys, serverctrls=self._server_controls,
clientctrls=self._client_controls)[0]
return entry.getValuesSet(keys)
+ def get_attrs_vals_utf8(self, keys, use_json=False):
+ self._log.debug("%s get_attrs_vals_utf8(%r)" % (self._dn, keys))
+ if self._instance.state != DIRSRV_STATE_ONLINE:
+ raise ValueError("Invalid state. Cannot get properties on instance that
is not ONLINE")
+ entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE,
self._object_filter, attrlist=keys, serverctrls=self._server_controls,
clientctrls=self._client_controls, escapehatch='i am sure')[0]
+ vset = entry.getValuesSet(keys)
+ r = {}
+ for (k, vo) in vset.items():
+ r[k] = ensure_list_str(vo)
+ return r
+
def get_attr_vals(self, key, use_json=False):
self._log.debug("%s get_attr_vals(%r)" % (self._dn, key))
# We might need to add a state check for NONE dn.
@@ -955,7 +966,7 @@ class DSLdapObjects(DSLogging):
# Filter based on the objectclasses and the basedn
# Based on the selector, we should filter on that too.
# This will yield and & filter for objectClass with as many terms as needed.
- filterstr=_gen_and([
+ filterstr = _gen_and([
self._get_objectclass_filter(),
_gen_or(
# This will yield all combinations of selector to filterattrs.
@@ -1024,5 +1035,3 @@ class DSLdapObjects(DSLogging):
(rdn, properties) = self._validate(rdn, properties)
# Now actually commit the creation req
return co.ensure_state(rdn, properties, self._basedn)
-
-
diff --git a/src/lib389/lib389/agreement.py b/src/lib389/lib389/agreement.py
index 128a607..cea9234 100644
--- a/src/lib389/lib389/agreement.py
+++ b/src/lib389/lib389/agreement.py
@@ -173,8 +173,10 @@ class Agreement(DSLdapObject):
consumer.allocate(args_standalone)
try:
consumer.open()
+ except ldap.INVALID_CREDENTIALS as e:
+ raise(e)
except ldap.LDAPError as e:
- self._instance.log.debug('Connection to consumer ({}:{}) failed, error:
{}'.format(host, port, e))
+ self._log.debug('Connection to consumer ({}:{}) failed, error:
{}'.format(host, port, e))
return result_msg
# Search for the tombstone RUV entry
@@ -182,7 +184,7 @@ class Agreement(DSLdapObject):
entry = consumer.search_s(suffix, ldap.SCOPE_SUBTREE,
REPLICA_RUV_FILTER, ['nsds50ruv'])
if not entry:
- self.log.error("Failed to retrieve database RUV entry from
consumer")
+ self._log.debug("Failed to retrieve database RUV entry from
consumer")
else:
elements = ensure_list_str(entry[0].getValues('nsds50ruv'))
for ruv in elements:
@@ -191,8 +193,10 @@ class Agreement(DSLdapObject):
if len(ruv_parts) == 5:
result_msg = ruv_parts[4]
break
+ except ldap.INVALID_CREDENTIALS as e:
+ raise(e)
except ldap.LDAPError as e:
- self._instance.log.debug('Failed to search for the suffix ' +
+ self._log.debug('Failed to search for the suffix ' +
'({}) consumer ({}:{}) failed, error:
{}'.format(
suffix, host, port, e))
consumer.close()
@@ -208,26 +212,31 @@ class Agreement(DSLdapObject):
"""
status = "Unknown"
- agmt_maxcsn = self.get_agmt_maxcsn()
- if agmt_maxcsn is not None:
- con_maxcsn = self.get_consumer_maxcsn(binddn=binddn, bindpw=bindpw)
- if con_maxcsn:
- if agmt_maxcsn == con_maxcsn:
- status = "In Synchronization"
- else:
- # Not in sync - attempt to discover the cause
- repl_msg = "Unknown"
- if self.get_attr_val_utf8(AGMT_UPDATE_IN_PROGRESS) ==
'TRUE':
- # Replication is on going - this is normal
- repl_msg = "Replication still in progress"
- elif "Can't Contact LDAP" in \
- self.get_attr_val_utf8(AGMT_UPDATE_STATUS):
- # Consumer is down
- repl_msg = "Consumer can not be contacted"
-
- status = ("Not in Synchronization: supplier " +
- "(%s) consumer (%s) Reason(%s)" %
- (agmt_maxcsn, con_maxcsn, repl_msg))
+ try:
+ agmt_maxcsn = self.get_agmt_maxcsn()
+ if agmt_maxcsn is not None:
+ con_maxcsn = self.get_consumer_maxcsn(binddn=binddn, bindpw=bindpw)
+ if con_maxcsn:
+ if agmt_maxcsn == con_maxcsn:
+ status = "In Synchronization"
+ else:
+ # Not in sync - attempt to discover the cause
+ repl_msg = "Unknown"
+ if self.get_attr_val_utf8_l(AGMT_UPDATE_IN_PROGRESS) ==
'true':
+ # Replication is on going - this is normal
+ repl_msg = "Replication still in progress"
+ elif "can't contact ldap" in \
+ self.get_attr_val_utf8_l(AGMT_UPDATE_STATUS):
+ # Consumer is down
+ repl_msg = "Consumer can not be contacted"
+
+ status = ("Not in Synchronization: supplier " +
+ "(%s) consumer (%s) Reason(%s)" %
+ (agmt_maxcsn, con_maxcsn, repl_msg))
+ except ldap.INVALID_CREDENTIALS as e:
+ raise(e)
+ except ldap.LDAPError as e:
+ raise ValueError(str(e))
return status
def get_lag_time(self, suffix, agmt_name, binddn=None, bindpw=None):
@@ -243,15 +252,20 @@ class Agreement(DSLdapObject):
:returns: A time-formated string of the the replication lag (HH:MM:SS).
:raises: ValueError - if unable to get consumer's maxcsn
"""
- agmt_time = 0
- agmt_maxcsn = self.get_agmt_maxcsn()
- con_maxcsn = self.get_consumer_maxcsn(binddn=binddn, bindpw=bindpw)
+
+ try:
+ agmt_maxcsn = self.get_agmt_maxcsn()
+ con_maxcsn = self.get_consumer_maxcsn(binddn=binddn, bindpw=bindpw)
+ except ldap.LDAPError as e:
+ raise ValueError("Unable to get lag time: " + str(e))
+
if con_maxcsn is None:
raise ValueError("Unable to get consumer's max csn")
- if con_maxcsn == "Unavailable":
+ if con_maxcsn.lower() == "unavailable":
return con_maxcsn
# Extract the csn timstamps and compare them
+ agmt_time = 0
match = Agreement.csnre.match(agmt_maxcsn)
if match:
agmt_time = int(match.group(1), 16)
@@ -292,6 +306,8 @@ class Agreement(DSLdapObject):
if not winsync:
try:
status = self.get_agmt_status(binddn=binddn, bindpw=bindpw)
+ except ldap.INVALID_CREDENTIALS as e:
+ raise(e)
except ValueError as e:
status = str(e)
if just_status:
@@ -305,6 +321,7 @@ class Agreement(DSLdapObject):
agmt_name = ensure_str(status_attrs_dict['cn'][0])
lag_time = self.get_lag_time(suffix, agmt_name, binddn=binddn,
bindpw=bindpw)
else:
+ lag_time = "Not available for Winsync agreements"
status = "Not available for Winsync agreements"
# handle the attributes that are not always set in the agreement
@@ -323,16 +340,21 @@ class Agreement(DSLdapObject):
if
ensure_str(status_attrs_dict['nsds5replicachangessentsincestartup'][0]) ==
'':
status_attrs_dict['nsds5replicachangessentsincestartup'] =
['0']
+ consumer =
"{}:{}".format(ensure_str(status_attrs_dict['nsds5replicahost'][0]),
+
ensure_str(status_attrs_dict['nsds5replicaport'][0]))
+
# Case sensitive?
if use_json:
- result = {'replica-enabled':
ensure_str(status_attrs_dict['nsds5replicaenabled'][0]),
+ result = {
+ 'agmt-name':
ensure_str(status_attrs_dict['cn'][0]),
+ 'replica': consumer,
+ 'replica-enabled':
ensure_str(status_attrs_dict['nsds5replicaenabled'][0]),
'update-in-progress':
ensure_str(status_attrs_dict['nsds5replicaupdateinprogress'][0]),
'last-update-start':
ensure_str(status_attrs_dict['nsds5replicalastupdatestart'][0]),
'last-update-end':
ensure_str(status_attrs_dict['nsds5replicalastupdateend'][0]),
'number-changes-sent':
ensure_str(status_attrs_dict['nsds5replicachangessentsincestartup'][0]),
'number-changes-skipped:':
ensure_str(status_attrs_dict['nsds5replicachangesskippedsince'][0]),
'last-update-status':
ensure_str(status_attrs_dict['nsds5replicalastupdatestatus'][0]),
- 'init-in-progress':
ensure_str(status_attrs_dict['nsds5beginreplicarefresh'][0]),
'last-init-start':
ensure_str(status_attrs_dict['nsds5replicalastinitstart'][0]),
'last-init-end':
ensure_str(status_attrs_dict['nsds5replicalastinitend'][0]),
'last-init-status':
ensure_str(status_attrs_dict['nsds5replicalastinitstatus'][0]),
@@ -343,8 +365,8 @@ class Agreement(DSLdapObject):
return (json.dumps(result))
else:
retstr = (
- "Status for %(cn)s agmt %(nsDS5ReplicaHost)s:"
- "%(nsDS5ReplicaPort)s" "\n"
+ "Status for agreement: \"%(cn)s\"
(%(nsDS5ReplicaHost)s:"
+ "%(nsDS5ReplicaPort)s)" "\n"
"Replica Enabled: %(nsds5ReplicaEnabled)s" "\n"
"Update In Progress: %(nsds5replicaUpdateInProgress)s"
"\n"
"Last Update Start: %(nsds5replicaLastUpdateStart)s"
"\n"
@@ -354,7 +376,6 @@ class Agreement(DSLdapObject):
"Number Of Changes Skipped: %(nsds5replicaChangesSkippedSince"
"Startup)s" "\n"
"Last Update Status: %(nsds5replicaLastUpdateStatus)s"
"\n"
- "Init In Progress: %(nsds5BeginReplicaRefresh)s"
"\n"
"Last Init Start: %(nsds5ReplicaLastInitStart)s"
"\n"
"Last Init End: %(nsds5ReplicaLastInitEnd)s" "\n"
"Last Init Status: %(nsds5ReplicaLastInitStatus)s"
"\n"
diff --git a/src/lib389/lib389/chaining.py b/src/lib389/lib389/chaining.py
index 35949d3..a25bbb6 100644
--- a/src/lib389/lib389/chaining.py
+++ b/src/lib389/lib389/chaining.py
@@ -102,17 +102,11 @@ class ChainingLink(DSLdapObject):
self._basedn = "cn=chaining database,cn=plugins,cn=config"
self._mts = MappingTrees(self._instance)
- def get_monitor(self, rdn):
+ def get_monitor(self):
"""Get a MonitorChaining(DSLdapObject) for the chaining link
- :param rdn - The 'cn' value of the chaining link
- :returns - chaining monitor entry"""
- links = ChainingLinks(self._instance).list()
- for link in links:
- cn = ensure_str(link.get_attr_val('cn')).lower()
- if cn == rdn.lower():
- monitor = MonitorChaining(instance=self._instance,
dn="cn=monitor,%s" % link._dn)
- return monitor
- return None
+ :returns - chaining monitor entry
+ """
+ return MonitorChaining(instance=self._instance, dn="cn=monitor,%s" %
self._dn)
def del_link(self):
"""
diff --git a/src/lib389/lib389/cli_base/__init__.py
b/src/lib389/lib389/cli_base/__init__.py
index 14cb999..ab1ce0f 100644
--- a/src/lib389/lib389/cli_base/__init__.py
+++ b/src/lib389/lib389/cli_base/__init__.py
@@ -194,11 +194,14 @@ def _generic_get_entry(inst, basedn, log, manager_class,
args=None):
def _generic_get_attr(inst, basedn, log, manager_class, args=None):
mc = manager_class(inst, basedn)
+ vals = {}
for attr in args.attrs:
if args and args.json:
- print(mc.get_attr_vals_json(attr))
+ vals[attr] = mc.get_attr_vals_utf8(attr)
else:
print(mc.display_attr(attr).rstrip())
+ if args.json:
+ print(json.dumps({"type": "entry", "dn": mc._dn,
"attrs": vals}))
def _generic_get_dn(inst, basedn, log, manager_class, dn, args=None):
@@ -312,7 +315,6 @@ def _generic_modify(inst, basedn, log, manager_class, selector,
args=None):
else:
raise ValueError("Missing modify actions to perform.")
-
class LogCapture(logging.Handler):
"""
This useful class is for intercepting logs, and then making assertions about
@@ -379,4 +381,3 @@ def setup_script_logger(name, verbose=False):
root.addHandler(log_handler)
return log
-
diff --git a/src/lib389/lib389/cli_conf/backend.py
b/src/lib389/lib389/cli_conf/backend.py
index 18da186..77ce3dd 100644
--- a/src/lib389/lib389/cli_conf/backend.py
+++ b/src/lib389/lib389/cli_conf/backend.py
@@ -1,5 +1,5 @@
# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2018 Red Hat, Inc.
+# Copyright (C) 2019 Red Hat, Inc.
# Copyright (C) 2019 William Brown <william(a)blackhats.net.au>
# All rights reserved.
#
@@ -303,7 +303,7 @@ def backend_get_subsuffixes(inst, basedn, log, args):
print("No sub-suffixes under this backend")
-def build_node(suffix, be_name, subsuf=False, link=False):
+def build_node(suffix, be_name, subsuf=False, link=False, replicated=False):
"""Build the UI node for a suffix
"""
icon = "glyphicon glyphicon-tree-conifer"
@@ -314,6 +314,9 @@ def build_node(suffix, be_name, subsuf=False, link=False):
if link:
icon = "glyphicon glyphicon-link"
suffix_type = "dblink"
+ if replicated:
+ suffix_type = "replicated"
+
return {
"text": suffix,
"id": suffix,
@@ -348,26 +351,29 @@ def backend_build_tree(inst, be_insts, nodes):
link = False
if is_db_link(inst, sub_be):
link = True
-
node['nodes'].append(build_node(mt.get_attr_val_utf8_l('cn'), sub_be,
subsuf=True, link=link))
+
node['nodes'].append(build_node(mt.get_attr_val_utf8_l('cn'),
+ sub_be,
+ subsuf=True,
+ link=link))
# Recurse over the new subsuffixes
backend_build_tree(inst, be_insts, node['nodes'])
break
-def print_suffix_tree(nodes, level):
+def print_suffix_tree(nodes, level, log):
"""Print all the nodes and children recursively
"""
if len(nodes) > 0:
for node in nodes:
spaces = " " * level
- print('{}- {}'.format(spaces, node['id']))
+ log.info('{}- {}'.format(spaces, node['id']))
if len(node['nodes']) > 0:
- print_suffix_tree(node['nodes'], level + 2)
+ print_suffix_tree(node['nodes'], level + 2, log)
def backend_get_tree(inst, basedn, log, args):
- """Build a tree model of all the suffixes/sub suffixes nad DB links
+ """Build a tree model of all the suffixes/sub suffixes and DB links
"""
nodes = []
@@ -376,30 +382,28 @@ def backend_get_tree(inst, basedn, log, args):
for be in be_insts:
suffix = be.get_attr_val_utf8_l('nsslapd-suffix')
be_name = be.get_attr_val_utf8('cn')
- try:
- mt = be._mts.get(suffix)
- except ldap.NO_SUCH_OBJECT:
- log.debug("Failed to find the mapping tree entry using this suffix:
{}".format(suffix))
- continue
+ mt = be._mts.get(suffix)
sub = mt.get_attr_val_utf8_l('nsslapd-parent-suffix')
if sub is not None:
- # Skip sub suffixes for now, we will get them later
continue
nodes.append(build_node(suffix, be_name))
# No suffixes, return empty list
if len(nodes) == 0:
- return nodes
-
- # Build the tree
- be_insts = Backends(inst).list()
- backend_build_tree(inst, be_insts, nodes)
-
- # Done
- if args.json:
- print(json.dumps(nodes))
+ if args.json:
+ log.info(json.dumps(nodes))
+ else:
+ log.info("There are no suffixes defined")
else:
- print_suffix_tree(nodes, 1)
+ # Build the tree
+ be_insts = Backends(inst).list()
+ backend_build_tree(inst, be_insts, nodes)
+
+ # Done
+ if args.json:
+ log.info(json.dumps(nodes))
+ else:
+ print_suffix_tree(nodes, 1, log)
def backend_set(inst, basedn, log, args):
@@ -1060,4 +1064,4 @@ def create_parser(subparsers):
# Get Suffix Tree (for use in web console)
#######################################################
get_tree_parser = subcommands.add_parser('get-tree', help='Get a
representation of the suffix tree')
- get_tree_parser.set_defaults(func=backend_get_tree)
\ No newline at end of file
+ get_tree_parser.set_defaults(func=backend_get_tree)
diff --git a/src/lib389/lib389/cli_conf/monitor.py
b/src/lib389/lib389/cli_conf/monitor.py
new file mode 100644
index 0000000..a704bea
--- /dev/null
+++ b/src/lib389/lib389/cli_conf/monitor.py
@@ -0,0 +1,88 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2019 William Brown <william(a)blackhats.net.au>
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+from lib389.monitor import (Monitor, MonitorLDBM, MonitorSNMP)
+from lib389.chaining import (ChainingLinks)
+from lib389.backend import Backends
+
+
+def _format_status(log, mtype, json=False):
+ if json:
+ print(mtype.get_status_json())
+ else:
+ status_dict = mtype.get_status()
+ log.info('dn: ' + mtype._dn)
+ for k, v in list(status_dict.items()):
+ # For each value in the multivalue attr
+ for vi in v:
+ log.info('{}: {}'.format(k, vi))
+
+
+def monitor(inst, basedn, log, args):
+ monitor = Monitor(inst)
+ _format_status(log, monitor, args.json)
+
+
+def backend_monitor(inst, basedn, log, args):
+ bes = Backends(inst)
+ if args.backend:
+ be = bes.get(args.backend)
+ be_monitor = be.get_monitor()
+ _format_status(log, be_monitor, args.json)
+ else:
+ for be in bes.list():
+ be_monitor = be.get_monitor()
+ _format_status(log, be_monitor, args.json)
+ # Inejct a new line for now ... see
https://pagure.io/389-ds-base/issue/50189
+ log.info("")
+
+
+def ldbm_monitor(inst, basedn, log, args):
+ ldbm_monitor = MonitorLDBM(inst)
+ _format_status(log, ldbm_monitor, args.json)
+
+
+def snmp_monitor(inst, basedn, log, args):
+ snmp_monitor = MonitorSNMP(inst)
+ _format_status(log, snmp_monitor, args.json)
+
+
+def chaining_monitor(inst, basedn, log, args):
+ links = ChainingLinks(inst)
+ if args.backend:
+ link = links.get(args.backend)
+ link_monitor = link.get_monitor()
+ _format_status(log, link_monitor, args.json)
+ else:
+ 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
+ log.info("")
+
+
+def create_parser(subparsers):
+ monitor_parser = subparsers.add_parser('monitor', help="Monitor the
state of the instance")
+ subcommands = monitor_parser.add_subparsers(help='action')
+
+ server_parser = subcommands.add_parser('server', help="Monitor the
server statistics, connectinos and operations")
+ server_parser.set_defaults(func=monitor)
+
+ ldbm_parser = subcommands.add_parser('ldbm', help="Monitor the ldbm
statistics, such as dbcache")
+ ldbm_parser.set_defaults(func=ldbm_monitor)
+
+ backend_parser = subcommands.add_parser('backend', help="Monitor the
behaviour of a backend database")
+ backend_parser.add_argument('backend', nargs='?', help="Optional
name of the backend to monitor")
+ backend_parser.set_defaults(func=backend_monitor)
+
+ snmp_parser = subcommands.add_parser('snmp', help="Monitor the SNMP
statistics")
+ snmp_parser.set_defaults(func=snmp_monitor)
+
+ 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)
\ No newline at end of file
diff --git a/src/lib389/lib389/cli_conf/replication.py
b/src/lib389/lib389/cli_conf/replication.py
index 96e7c02..b25eba6 100644
--- a/src/lib389/lib389/cli_conf/replication.py
+++ b/src/lib389/lib389/cli_conf/replication.py
@@ -16,7 +16,6 @@ from lib389.replica import Replicas, BootstrapReplicationManager
from lib389.tasks import CleanAllRUVTask, AbortCleanAllRUVTask
from lib389._mapped_object import DSLdapObjects
-
arg_to_attr = {
# replica config
'replica_id': 'nsds5replicaid',
@@ -196,7 +195,7 @@ def enable_replication(inst, basedn, log, args):
# Some other bad error
raise ValueError("Failed to create replication manager entry: " +
str(e))
- print("Replication successfully enabled for
\"{}\"".format(repl_root))
+ log.info("Replication successfully enabled for
\"{}\"".format(repl_root))
def disable_replication(inst, basedn, log, args):
@@ -206,7 +205,7 @@ def disable_replication(inst, basedn, log, args):
replica.delete()
except ldap.NO_SUCH_OBJECT:
raise ValueError("Backend \"{}\" is not enabled for
replication".format(args.suffix))
- print("Replication disabled for \"{}\"".format(args.suffix))
+ log.info("Replication disabled for \"{}\"".format(args.suffix))
def promote_replica(inst, basedn, log, args):
@@ -224,7 +223,7 @@ def promote_replica(inst, basedn, log, args):
raise ValueError("Invalid role ({}), you must use either
\"master\" or \"hub\"".format(role))
replica.promote(newrole, binddn=args.bind_dn, binddn_group=args.bind_group_dn,
rid=args.replica_id)
- print("Successfully promoted replica to \"{}\"".format(role))
+ log.info("Successfully promoted replica to \"{}\"".format(role))
def demote_replica(inst, basedn, log, args):
@@ -240,7 +239,7 @@ def demote_replica(inst, basedn, log, args):
raise ValueError("Invalid role ({}), you must use either \"hub\"
or \"consumer\"".format(role))
replica.demote(newrole)
- print("Successfully demoted replica to \"{}\"".format(role))
+ log.info("Successfully demoted replica to \"{}\"".format(role))
def list_suffixes(inst, basedn, log, args):
@@ -250,22 +249,44 @@ def list_suffixes(inst, basedn, log, args):
suffixes.append(replica.get_suffix())
if args.json:
- print(json.dumps({"type": "list", "items":
suffixes}))
+ log.info(json.dumps({"type": "list", "items":
suffixes}))
else:
if len(suffixes) == 0:
- print("There are no replicated suffixes")
+ log.info("There are no replicated suffixes")
else:
for suffix in suffixes:
- print(suffix)
+ log.info(suffix)
+
+
+def get_repl_status(inst, basedn, log, args):
+ replicas = Replicas(inst)
+ replica = replicas.get(args.suffix)
+ status = replica.status(binddn=args.bind_dn, bindpw=args.bind_passwd)
+ if args.json:
+ log.info(json.dumps({"type": "list", "items":
status}))
+ else:
+ for agmt in status:
+ log.info(agmt)
+
+
+def get_repl_winsync_status(inst, basedn, log, args):
+ replicas = Replicas(inst)
+ replica = replicas.get(args.suffix)
+ status = replica.status(binddn=args.bind_dn, bindpw=args.bind_passwd, winsync=True)
+ if args.json:
+ log.info(json.dumps({"type": "list", "items":
status}))
+ else:
+ for agmt in status:
+ log.info(agmt)
def get_repl_config(inst, basedn, log, args):
replicas = Replicas(inst)
replica = replicas.get(args.suffix)
if args and args.json:
- print(replica.get_all_attrs_json())
+ log.info(replica.get_all_attrs_json())
else:
- print(replica.display())
+ log.info(replica.display())
def set_repl_config(inst, basedn, log, args):
@@ -311,7 +332,7 @@ def set_repl_config(inst, basedn, log, args):
elif not did_something:
raise ValueError("There are no changes to set in the replica")
- print("Successfully updated replication configuration")
+ log.info("Successfully updated replication configuration")
def create_cl(inst, basedn, log, args):
@@ -323,7 +344,7 @@ def create_cl(inst, basedn, log, args):
})
except ldap.ALREADY_EXISTS:
raise ValueError("Changelog already exists")
- print("Successfully created replication changelog")
+ log.info("Successfully created replication changelog")
def delete_cl(inst, basedn, log, args):
@@ -332,7 +353,7 @@ def delete_cl(inst, basedn, log, args):
cl.delete()
except ldap.NO_SUCH_OBJECT:
raise ValueError("There is no changelog to delete")
- print("Successfully deleted replication changelog")
+ log.info("Successfully deleted replication changelog")
def set_cl(inst, basedn, log, args):
@@ -351,15 +372,15 @@ def set_cl(inst, basedn, log, args):
elif not did_something:
raise ValueError("There are no changes to set for the replication
changelog")
- print("Successfully updated replication changelog")
+ log.info("Successfully updated replication changelog")
def get_cl(inst, basedn, log, args):
cl = Changelog5(inst)
if args and args.json:
- print(cl.get_all_attrs_json())
+ log.info(cl.get_all_attrs_json())
else:
- print(cl.display())
+ log.info(cl.display())
def create_repl_manager(inst, basedn, log, args):
@@ -392,7 +413,7 @@ def create_repl_manager(inst, basedn, log, args):
if repl_manager_password_confirm == repl_manager_password:
break
else:
- print("Passwords do not match!\n")
+ log.info("Passwords do not match!\n")
repl_manager_password = ""
repl_manager_password_confirm = ""
@@ -410,9 +431,9 @@ def create_repl_manager(inst, basedn, log, args):
replica.add('nsds5ReplicaBindDN', manager_dn)
except ldap.TYPE_OR_VALUE_EXISTS:
pass
- print("Successfully created replication manager: " + manager_dn)
+ log.info("Successfully created replication manager: " + manager_dn)
except ldap.ALREADY_EXISTS:
- print("Replication Manager ({}) already exists, recreating
it...".format(manager_dn))
+ log.info("Replication Manager ({}) already exists, recreating
it...".format(manager_dn))
# Already there, but could have different password. Delete and recreate
manager.delete()
manager.create(properties={
@@ -428,7 +449,7 @@ def create_repl_manager(inst, basedn, log, args):
except ldap.TYPE_OR_VALUE_EXISTS:
pass
- print("Successfully created replication manager: " + manager_dn)
+ log.info("Successfully created replication manager: " + manager_dn)
def del_repl_manager(inst, basedn, log, args):
@@ -443,7 +464,7 @@ def del_repl_manager(inst, basedn, log, args):
replicas = Replicas(inst)
replica = replicas.get(args.suffix)
replica.remove('nsds5ReplicaBindDN', manager_dn)
- print("Successfully deleted replication manager: " + manager_dn)
+ log.info("Successfully deleted replication manager: " + manager_dn)
#
@@ -462,9 +483,9 @@ def list_agmts(inst, basedn, log, args):
# Append decoded json object, because we are going to dump it later
result['items'].append(json.loads(entry))
else:
- print(agmt.display())
+ log.info(agmt.display())
if args.json:
- print(json.dumps(result))
+ log.info(json.dumps(result))
def add_agmt(inst, basedn, log, args):
@@ -524,7 +545,7 @@ def add_agmt(inst, basedn, log, args):
except ldap.ALREADY_EXISTS:
raise ValueError("A replication agreement with the same name already
exists")
- print("Successfully created replication agreement
\"{}\"".format(get_agmt_name(args)))
+ log.info("Successfully created replication agreement
\"{}\"".format(get_agmt_name(args)))
if args.init:
init_agmt(inst, basedn, log, args)
@@ -532,25 +553,25 @@ def add_agmt(inst, basedn, log, args):
def delete_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args)
agmt.delete()
- print("Agreement has been successfully deleted")
+ log.info("Agreement has been successfully deleted")
def enable_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args)
agmt.resume()
- print("Agreement has been enabled")
+ log.info("Agreement has been enabled")
def disable_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args)
agmt.pause()
- print("Agreement has been disabled")
+ log.info("Agreement has been disabled")
def init_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args)
agmt.begin_reinit()
- print("Agreement initialization started...")
+ log.info("Agreement initialization started...")
def check_init_agmt(inst, basedn, log, args):
@@ -564,9 +585,9 @@ def check_init_agmt(inst, basedn, log, args):
elif error:
status = "Agreement initialization failed."
if args.json:
- print(json.dumps(status))
+ log.info(json.dumps(status))
else:
- print(status)
+ log.info(status)
def set_agmt(inst, basedn, log, args):
@@ -592,15 +613,15 @@ def set_agmt(inst, basedn, log, args):
elif not did_something:
raise ValueError("There are no changes to set in the agreement")
- print("Successfully updated agreement")
+ log.info("Successfully updated agreement")
def get_repl_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args)
if args.json:
- print(agmt.get_all_attrs_json())
+ log.info(agmt.get_all_attrs_json())
else:
- print(agmt.display())
+ log.info(agmt.display())
def poke_agmt(inst, basedn, log, args):
@@ -608,7 +629,7 @@ def poke_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args)
agmt.pause()
agmt.resume()
- print("Agreement has been poked")
+ log.info("Agreement has been poked")
def get_agmt_status(inst, basedn, log, args):
@@ -618,7 +639,7 @@ def get_agmt_status(inst, basedn, log, args):
while args.bind_passwd == "":
args.bind_passwd = getpass("Enter password for \"{}\":
".format(args.bind_dn))
status = agmt.status(use_json=args.json, binddn=args.bind_dn,
bindpw=args.bind_passwd)
- print(agmt, status)
+ log.info(status)
#
@@ -637,9 +658,9 @@ def list_winsync_agmts(inst, basedn, log, args):
# Append decoded json object, because we are going to dump it later
result['items'].append(json.loads(entry))
else:
- print(agmt.display())
+ log.info(agmt.display())
if args.json:
- print(json.dumps(result))
+ log.info(json.dumps(result))
def add_winsync_agmt(inst, basedn, log, args):
@@ -698,7 +719,7 @@ def add_winsync_agmt(inst, basedn, log, args):
except ldap.ALREADY_EXISTS:
raise ValueError("A replication agreement with the same name already
exists")
- print("Successfully created winsync replication agreement
\"{}\"".format(get_agmt_name(args)))
+ log.info("Successfully created winsync replication agreement
\"{}\"".format(get_agmt_name(args)))
if args.init:
init_winsync_agmt(inst, basedn, log, args)
@@ -706,7 +727,7 @@ def add_winsync_agmt(inst, basedn, log, args):
def delete_winsync_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args, winsync=True)
agmt.delete()
- print("Agreement has been successfully deleted")
+ log.info("Agreement has been successfully deleted")
def set_winsync_agmt(inst, basedn, log, args):
@@ -727,25 +748,25 @@ def set_winsync_agmt(inst, basedn, log, args):
elif not did_something:
raise ValueError("There are no changes to set in the agreement")
- print("Successfully updated agreement")
+ log.info("Successfully updated agreement")
def enable_winsync_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args, winsync=True)
agmt.resume()
- print("Agreement has been enabled")
+ log.info("Agreement has been enabled")
def disable_winsync_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args, winsync=True)
agmt.pause()
- print("Agreement has been disabled")
+ log.info("Agreement has been disabled")
def init_winsync_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args, winsync=True)
agmt.begin_reinit()
- print("Agreement initialization started...")
+ log.info("Agreement initialization started...")
def check_winsync_init_agmt(inst, basedn, log, args):
@@ -759,17 +780,17 @@ def check_winsync_init_agmt(inst, basedn, log, args):
elif error:
status = "Agreement initialization failed."
if args.json:
- print(json.dumps(status))
+ log.info(json.dumps(status))
else:
- print(status)
+ log.info(status)
def get_winsync_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args, winsync=True)
if args.json:
- print(agmt.get_all_attrs_json())
+ log.info(agmt.get_all_attrs_json())
else:
- print(agmt.display())
+ log.info(agmt.display())
def poke_winsync_agmt(inst, basedn, log, args):
@@ -777,13 +798,13 @@ def poke_winsync_agmt(inst, basedn, log, args):
agmt = get_agmt(inst, args, winsync=True)
agmt.pause()
agmt.resume()
- print("Agreement has been poked")
+ log.info("Agreement has been poked")
def get_winsync_agmt_status(inst, basedn, log, args):
agmt = get_agmt(inst, args, winsync=True)
status = agmt.status(winsync=True, use_json=args.json)
- print(agmt, status)
+ log.info(status)
#
@@ -798,9 +819,9 @@ def run_cleanallruv(inst, basedn, log, args):
clean_task.create(properties=properties)
rdn = clean_task.rdn
if args.json:
- print(json.dumps(rdn))
+ log.info(json.dumps(rdn))
else:
- print('Created task ' + rdn)
+ log.info('Created task ' + rdn)
def list_cleanallruv(inst, basedn, log, args):
@@ -813,17 +834,20 @@ def list_cleanallruv(inst, basedn, log, args):
tasks_found = False
for task in tasks:
tasks_found = True
+ if args.suffix is not None:
+ if args.suffix.lower() !=
task.get_attr_val_utf8_l('replica-base-dn'):
+ continue
if args.json:
entry = task.get_all_attrs_json()
# Append decoded json object, because we are going to dump it later
result['items'].append(json.loads(entry))
else:
- print(task.display())
+ log.info(task.display())
if args.json:
- print(json.dumps(result))
+ log.info(json.dumps(result))
else:
if not tasks_found:
- print("No CleanAllRUV tasks found")
+ log.info("No CleanAllRUV tasks found")
def abort_cleanallruv(inst, basedn, log, args):
@@ -835,6 +859,32 @@ def abort_cleanallruv(inst, basedn, log, args):
clean_task.create(properties=properties)
+def list_abort_cleanallruv(inst, basedn, log, args):
+ tasksobj = DSLdapObjects(inst)
+ tasksobj._basedn = "cn=abort cleanallruv, cn=tasks, cn=config"
+ tasksobj._scope = ldap.SCOPE_ONELEVEL
+ tasksobj._objectclasses = ['top']
+ tasks = tasksobj.list()
+ result = {"type": "list", "items": []}
+ tasks_found = False
+ for task in tasks:
+ tasks_found = True
+ if args.suffix is not None:
+ if args.suffix.lower() !=
task.get_attr_val_utf8_l('replica-base-dn'):
+ continue
+ if args.json:
+ entry = task.get_all_attrs_json()
+ # Append decoded json object, because we are going to dump it later
+ result['items'].append(json.loads(entry))
+ else:
+ log.info(task.display())
+ if args.json:
+ log.info(json.dumps(result))
+ else:
+ if not tasks_found:
+ log.info("No CleanAllRUV abort tasks found")
+
+
def create_parser(subparsers):
############################################
@@ -860,6 +910,18 @@ def create_parser(subparsers):
repl_list_parser = repl_subcommands.add_parser('list', help='List all the
replicated suffixes')
repl_list_parser.set_defaults(func=list_suffixes)
+ repl_status_parser = repl_subcommands.add_parser('status', help='Get the
current status of all the replication agreements')
+ repl_status_parser.set_defaults(func=get_repl_status)
+ repl_status_parser.add_argument('--suffix', required=True, help="The DN
of the replication suffix")
+ repl_status_parser.add_argument('--bind-dn', help="The DN to use to
authenticate to the consumer")
+ repl_status_parser.add_argument('--bind-passwd', help="The password for
the bind DN")
+
+ repl_winsync_status_parser = repl_subcommands.add_parser('winsync-status',
help='Get the current status of all the replication agreements')
+ repl_winsync_status_parser.set_defaults(func=get_repl_winsync_status)
+ repl_winsync_status_parser.add_argument('--suffix', required=True,
help="The DN of the replication suffix")
+ repl_winsync_status_parser.add_argument('--bind-dn', help="The DN to use
to authenticate to the consumer")
+ repl_winsync_status_parser.add_argument('--bind-passwd', help="The
password for the bind DN")
+
repl_promote_parser = repl_subcommands.add_parser('promote', help='Promte
replica to a Hub or Master')
repl_promote_parser.set_defaults(func=promote_replica)
repl_promote_parser.add_argument('--suffix', required=True, help="The DN
of the replication suffix to promote")
@@ -981,7 +1043,7 @@ def create_parser(subparsers):
agmt_status_parser.set_defaults(func=get_agmt_status)
agmt_status_parser.add_argument('AGMT_NAME', nargs=1, help='The name of
the replication agreement')
agmt_status_parser.add_argument('--suffix', required=True, help="The DN
of the replication suffix")
- agmt_status_parser.add_argument('--bind-dn', help="Set the DN to bind to
the consumer")
+ agmt_status_parser.add_argument('--bind-dn', help="The DN to use to
authenticate to the consumer")
agmt_status_parser.add_argument('--bind-passwd', help="The password for
the bind DN")
# Delete
@@ -1187,8 +1249,9 @@ def create_parser(subparsers):
task_cleanallruv.add_argument('--force-cleaning',
action='store_true', default=False,
help="Ignore errors and do a best attempt to clean
all the replicas")
- task_cleanallruv_list = task_subcommands.add_parser('list-cleanallruv',
help='List all the running CleanAllRUV Tasks')
+ task_cleanallruv_list = task_subcommands.add_parser('list-cleanruv-tasks',
help='List all the running CleanAllRUV tasks')
task_cleanallruv_list.set_defaults(func=list_cleanallruv)
+ task_cleanallruv_list.add_argument('--suffix', help="List only tasks
from for suffix")
# Abort cleanallruv
task_abort_cleanallruv = task_subcommands.add_parser('abort-cleanallruv',
help='Abort cleanallruv tasks')
@@ -1198,4 +1261,7 @@ def create_parser(subparsers):
task_abort_cleanallruv.add_argument('--certify', action='store_true',
default=False,
help="Enforce that the abort task completed
on all replicas")
+ task_abort_cleanallruv_list =
task_subcommands.add_parser('list-abortruv-tasks', help='List all the running
CleanAllRUV abort Tasks')
+ task_abort_cleanallruv_list.set_defaults(func=list_abort_cleanallruv)
+ task_abort_cleanallruv_list.add_argument('--suffix', help="List only
tasks from for suffix")
diff --git a/src/lib389/lib389/monitor.py b/src/lib389/lib389/monitor.py
index 0102630..9c11d34 100644
--- a/src/lib389/lib389/monitor.py
+++ b/src/lib389/lib389/monitor.py
@@ -1,16 +1,14 @@
# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2015 Red Hat, Inc.
+# Copyright (C) 2019 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
-import ldap
-from ldap import filter as ldap_filter
from lib389._constants import *
-from lib389._mapped_object import DSLdapObjects, DSLdapObject
-from lib389.utils import (ds_is_older, ensure_str)
+from lib389._mapped_object import DSLdapObject
+from lib389.utils import (ds_is_older)
class Monitor(DSLdapObject):
@@ -105,19 +103,16 @@ class MonitorLDBM(DSLdapObject):
'normalizeddncachehits',
'normalizeddncachemisses',
'normalizeddncachehitratio',
+ 'normalizeddncacheevictions',
'currentnormalizeddncachesize',
'maxnormalizeddncachesize',
- 'currentnormalizeddncachecount'
+ 'currentnormalizeddncachecount',
+ 'normalizeddncachethreadsize',
+ 'normalizeddncachethreadslots'
])
def get_status(self, use_json=False):
- if use_json:
- print(self.get_attrs_vals_json(self._backend_keys))
- else:
- attr_dict = self.get_attrs_vals(self._backend_keys)
- print('dn: ' + self._dn)
- for k, v in list(attr_dict.items()):
- print('{}: {}'.format(k, ensure_str(v[0])))
+ return self.get_attrs_vals_utf8(self._backend_keys)
class MonitorBackend(DSLdapObject):
@@ -156,13 +151,7 @@ class MonitorBackend(DSLdapObject):
])
def get_status(self, use_json=False):
- if use_json:
- print(self.get_attrs_vals_json(self._backend_keys))
- else:
- attr_dict = self.get_attrs_vals(self._backend_keys)
- print('dn: ' + self._dn)
- for k, v in list(attr_dict.items()):
- print('{}: {}'.format(k, ensure_str(v[0])))
+ return self.get_attrs_vals_utf8(self._backend_keys)
class MonitorChaining(DSLdapObject):
@@ -188,10 +177,50 @@ class MonitorChaining(DSLdapObject):
self._protected = False
def get_status(self, use_json=False):
- if use_json:
- print(self.get_attrs_vals_json(self._chaining_keys))
- else:
- attr_dict = self.get_attrs_vals(self._chaining_keys)
- print('dn: ' + self._dn)
- for k, v in list(attr_dict.items()):
- print('{}: {}'.format(k, ensure_str(v[0])))
+ return self.get_attrs_vals_utf8(self._chaining_keys)
+
+
+class MonitorSNMP(DSLdapObject):
+ """
+ """
+ def __init__(self, instance, dn=None):
+ super(MonitorSNMP, self).__init__(instance=instance, dn=dn)
+ self._dn = DN_MONITOR_SNMP
+ self._snmp_keys = [
+ 'anonymousbinds',
+ 'unauthbinds',
+ 'simpleauthbinds',
+ 'strongauthbinds',
+ 'bindsecurityerrors',
+ 'inops',
+ 'readops',
+ 'compareops',
+ 'addentryops',
+ 'removeentryops',
+ 'modifyentryops',
+ 'modifyrdnops',
+ 'listops',
+ 'searchops',
+ 'onelevelsearchops',
+ 'wholesubtreesearchops',
+ 'referrals',
+ 'chainings',
+ 'securityerrors',
+ 'errors',
+ 'connections',
+ 'connectionseq',
+ 'connectionsinmaxthreads',
+ 'connectionsmaxthreadscount',
+ 'bytesrecv',
+ 'bytessent',
+ 'entriesreturned',
+ 'referralsreturned',
+ 'masterentries',
+ 'copyentries',
+ 'cacheentries',
+ 'cachehits',
+ 'slavehits'
+ ]
+
+ def get_status(self, use_json=False):
+ return self.get_attrs_vals_utf8(self._snmp_keys)
diff --git a/src/lib389/lib389/replica.py b/src/lib389/lib389/replica.py
index a462c98..333645b 100644
--- a/src/lib389/lib389/replica.py
+++ b/src/lib389/lib389/replica.py
@@ -11,7 +11,8 @@ import decimal
import time
import logging
import uuid
-
+import json
+from operator import itemgetter
from itertools import permutations
from lib389._constants import *
from lib389.properties import *
@@ -22,6 +23,7 @@ from lib389.passwd import password_generate
from lib389.mappingTree import MappingTrees
from lib389.agreement import Agreements
from lib389.changelog import Changelog5
+from lib389.tombstone import Tombstones
from lib389.idm.domain import Domain
from lib389.idm.group import Groups
@@ -1250,6 +1252,25 @@ class Replica(DSLdapObject):
return self._suffix
+ def status(self, binddn=None, bindpw=None, winsync=False):
+ """Get a list of the status for every agreement
+ """
+ agmtList = []
+ agmts = Agreements(self._instance, self.dn, winsync=winsync).list()
+ for agmt in agmts:
+ raw_status = agmt.status(binddn=binddn, bindpw=bindpw, use_json=True,
winsync=winsync)
+ agmtList.append(json.loads(raw_status))
+
+ # sort the list of agreements by the lag time
+ sortedList = sorted(agmtList, key=itemgetter('replication-lag-time'))
+ return(sortedList)
+
+ def get_tombstone_count(self):
+ """Get the number of tombstones
+ """
+ tombstones = Tombstones(self._instance, self._suffix).list()
+ return len(tombstones)
+
class Replicas(DSLdapObjects):
"""Replica DSLdapObjects for all replicas
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.