#50323 Ticket 50291 - Add monitor tab functionality to Cockpit UI
Closed 3 years ago by spichugi. Opened 5 years ago by mreynolds.
mreynolds/389-ds-base ticket50291  into  master

@@ -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 @@ 

      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 @@ 

      max-width: 600px;

  }

  

+ .ds-lag-report {

+     min-width: 800px !important;

+ }

+ 

  .ds-repl-mgr {

      max-width: 600px !important;

  }
@@ -578,10 +607,7 @@ 

  }

  

  .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 @@ 

  

  .ds-table-header th {

      text-align: center !important;

-     background-color: white;

  }

  

  .ds-plugin-table {
@@ -1058,13 +1083,13 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

      position: relative;

  }

  

- tbody:nth-child(even) {

-     background: #f1f1f1;

- }

- 

  .ds-toolbar {

      bottom: 10px;

  }
@@ -1546,8 +1573,7 @@ 

  }

  

  .ds-table-pagination {

-     background-color: transparent !important;

-     border: 1px solid #909090;

+     border: 1px solid #d1d1d1;

  }

  

  .ds-vlv-label {
@@ -1587,12 +1613,16 @@ 

      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 @@ 

      border-color: #bee1f4;

      display: block;

  }

+ 

+ input {

+     padding-left: 5px !important;

+ }

+ 

+ .ds-width-auto {

+     width: 100%;

+ }

@@ -185,9 +185,10 @@ 

                          }), 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 @@ 

                      ), 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 @@ 

                      });

                  })

                  .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 @@ 

                      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 @@ 

                      });

                  })

                  .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 @@ 

              "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 @@ 

                                                          });

                                                      })

                                                      .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 @@ 

                      });

                  })

                  .fail(err => {

+                     let errMsg = JSON.parse(err);

                      this.addNotification(

                          "error",

-                         `Failed to get attributes - ${err}`

+                         `Failed to get attributes - ${errMsg.desc}`

                      );

                  });

      }

@@ -13,7 +13,7 @@ 

  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 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) {

You've added the function here and in tools.jsx. But this one is unused in your code... Was it done for some future code?

+     // 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 () {
@@ -446,4 +478,8 @@ 

      $(".all-pages").hide();

      $("#database-content").show();

    });

+   $("#monitor-tab").on("click", function() {

+     $(".all-pages").hide();

+     $("#monitor-content").show();

+   });

  });

@@ -2,6 +2,7 @@ 

  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 @@ 

                  .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

@@ -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>

@@ -74,10 +74,11 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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}`

                      );

                  });

      }

@@ -251,10 +251,11 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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 @@ 

                      });

                  })

                  .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,

@@ -195,10 +195,11 @@ 

                          );

                      })

                      .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 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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 @@ 

                          );

                      })

                      .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 @@ 

                      );

                  })

                  .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 @@ 

                              </div>

                          </Form>

                      </Modal.Body>

-                     <Modal.Footer className="ds-modal-footerZZZ">

+                     <Modal.Footer>

                          <Button

                              bsStyle="default"

                              className="btn-cancel"
@@ -1221,7 +1228,7 @@ 

                              </div>

                          </Form>

                      </Modal.Body>

-                     <Modal.Footer className="ds-modal-footer">

+                     <Modal.Footer>

                          <Button

                              bsStyle="default"

                              className="btn-cancel"

@@ -190,10 +190,11 @@ 

                          );

                      })

                      .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 @@ 

                              <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>

@@ -132,17 +132,19 @@ 

                                              }

                                          })

                                          .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 @@ 

                      );

                  })

                  .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 @@ 

                      });

                  })

                  .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 @@ 

                          }

                      })

                      .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 @@ 

                      );

                  })

                  .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}`

                      );

                  });

      }

@@ -90,10 +90,11 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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}`

                      );

                  });

      }

@@ -196,9 +196,10 @@ 

                      });

                  })

                  .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 @@ 

                      });

                  })

                  .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 @@ 

                      });

                  })

                  .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 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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 @@ 

                          );

                      })

                      .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}`

                          );

                      });

          }

@@ -224,11 +224,12 @@ 

                          );

                      })

                      .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 @@ 

                          );

                      })

                      .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 @@ 

                          );

                      })

                      .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 @@ 

                                      );

                                  })

                                  .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 @@ 

                      );

                  })

                  .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 @@ 

                      );

                  })

                  .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}`

                      );

                  });

      }

@@ -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;

@@ -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;

@@ -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;

@@ -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;

@@ -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;

@@ -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;

@@ -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,

+ };

@@ -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;

@@ -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;

@@ -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;

@@ -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;

@@ -25,7 +25,7 @@ 

                                  <strong>{notification.header}</strong>

                              )}

                              {notification.type == "error" ? (

-                                 <pre>{notification.message}</pre>

+                                 <pre className="ds-width-auto">{notification.message}</pre>

                              ) : (

                                  <span>{notification.message}</span>

                              )}

@@ -56,3 +56,50 @@ 

          );

      }

  }

+ 

+ // 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`;

+ }

@@ -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>

@@ -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 = "&nbsp;<font size=\"2\" color=\"red\"><b>(Agreement Disabled)</b></font>";

-       }

-       // clear_agmt_form();

- 

-       $("#monitor-agmt-header").html("<b>Replication Agreement Details:</b>&nbsp;&nbsp; " + 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 = "&nbsp;<font size=\"2\" color=\"red\"><b>(Agreement Disabled)</b></font>";

-       }

-       // clear_agmt_form();

- 

-       $("#repl-winsync-agmt-header").html("<b>Winsync Agreement Details:</b>&nbsp;&nbsp; " + 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;

-   });

- });

The added file is too large to be shown here, see it at: src/cockpit/389-console/src/monitor.jsx
@@ -182,7 +182,8 @@ 

                  })

                  .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 @@ 

                      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 @@ 

                                      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 @@ 

                                          }

                                          this.addNotification(

                                              "error",

-                                             `${err.message} error during ${data.name} modification`

+                                             `${errMsg.desc} error during ${data.name} modification`

                                          );

                                      }

                                      this.toggleLoading();

@@ -181,8 +181,6 @@ 

  <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 @@ 

                </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 @@ 

              </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 @@ 

              </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 @@ 

              </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 @@ 

              </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 @@ 

              </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>

@@ -347,7 +347,7 @@ 

  

  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 @@ 

        $("#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 @@ 

        }

        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();

@@ -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>

@@ -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>

@@ -30,8 +30,6 @@ 

          "fonts",

          "images",

          "index.html",

-         "monitor.html",

-         "monitor.js",

          "replication.html",

          "replication.js",

          "rhds-banner.html",

file modified
+9 -4
@@ -15,6 +15,8 @@ 

  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
@@ -134,10 +136,15 @@ 

              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)
@@ -145,5 +152,3 @@ 

      # Done!

      if result is False:

          sys.exit(1)

- 

- 

@@ -535,7 +535,6 @@ 

              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.
@@ -1047,7 +1046,7 @@ 

          # 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=self._get_selector_filter(selector)

+         filterstr = self._get_selector_filter(selector)

          self._log.debug('_gen_selector filter = %s' % filterstr)

          return self._instance.search_ext_s(

              base=self._basedn,

file modified
+53 -32
@@ -173,8 +173,10 @@ 

          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 @@ 

              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 @@ 

                          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 @@ 

          """

          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 @@ 

          :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 @@ 

          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 @@ 

              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 @@ 

          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 @@ 

              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 @@ 

                  "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"

file modified
+4 -10
@@ -102,17 +102,11 @@ 

          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):

          """

@@ -194,11 +194,14 @@ 

  

  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):
@@ -329,7 +332,6 @@ 

      _generic_modify_inner(log, o, args.changes)

  

  

- 

  class LogCapture(logging.Handler):

      """

      This useful class is for intercepting logs, and then making assertions about
@@ -396,4 +398,3 @@ 

      root.addHandler(log_handler)

  

      return log

- 

@@ -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@blackhats.net.au>

  # All rights reserved.

  #
@@ -303,7 +303,7 @@ 

              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 @@ 

      if link:

          icon = "glyphicon glyphicon-link"

          suffix_type = "dblink"

+     if replicated:

+         suffix_type = "replicated"

+ 

      return {

          "text": suffix,

          "id": suffix,
@@ -348,26 +351,29 @@ 

                          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 @@ 

      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 @@ 

      # 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)

@@ -6,10 +6,10 @@ 

  # See LICENSE for details.

  # --- END COPYRIGHT BLOCK ---

  

- from lib389.monitor import Monitor, MonitorLDBM

+ from lib389.monitor import (Monitor, MonitorLDBM, MonitorSNMP)

+ from lib389.chaining import (ChainingLinks)

  from lib389.backend import Backends

  

- from lib389.utils import ensure_str

  

  def _format_status(log, mtype, json=False):

      if json:
@@ -33,23 +33,41 @@ 

      if args.backend:

          be = bes.get(args.backend)

          be_monitor = be.get_monitor()

-         be_monitor.get_status()

+         _format_status(log, be_monitor, args.json)

      else:

          for be in bes.list():

              be_monitor = be.get_monitor()

-             be_monitor.get_status()

+             _format_status(log, be_monitor, args.json)

              # Inejct a new line for now ... see https://pagure.io/389-ds-base/issue/50189

-             print("")

+             log.info("")

  

  

  def ldbm_monitor(inst, basedn, log, args):

      ldbm_monitor = MonitorLDBM(inst)

-     ldbm_monitor.get_status()

+     _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")
@@ -62,3 +80,9 @@ 

      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

@@ -16,7 +16,6 @@ 

  from lib389.tasks import CleanAllRUVTask, AbortCleanAllRUVTask

  from lib389._mapped_object import DSLdapObjects

  

- 

  arg_to_attr = {

          # replica config

          'replica_id': 'nsds5replicaid',
@@ -196,7 +195,7 @@ 

              # 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 @@ 

          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 @@ 

          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 @@ 

          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 @@ 

          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 @@ 

      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 @@ 

          })

      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 @@ 

          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 @@ 

      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 @@ 

              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 @@ 

                  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 @@ 

              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 @@ 

          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 @@ 

              # 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 @@ 

      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 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 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

          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 @@ 

              # 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 @@ 

      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 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 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

      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 @@ 

      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")

  

file modified
+56 -27
@@ -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):
@@ -125,19 +123,16 @@ 

                  '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):
@@ -178,13 +173,7 @@ 

      # Issue: get status should return a dict and the called should be

      # formatting it. See: https://pagure.io/389-ds-base/issue/50189

      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):
@@ -210,10 +199,50 @@ 

          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)

file modified
+25 -4
@@ -11,7 +11,8 @@ 

  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.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
@@ -130,7 +132,7 @@ 

                       'passwordExpirationTime': '20381010000000Z'}

              self.conn.setupBindDN(repl_manager_dn, repl_manager_pw, attrs)

          except ldap.ALREADY_EXISTS:

-             self.log.warning("User already exists (weird we just checked: %s ",

+             self.log.warn("User already exists (weird we just checked: %s ",

                            repl_manager_dn)

  

      def list(self, suffix=None, replica_dn=None):
@@ -384,7 +386,7 @@ 

          dn_replica = ','.join((RDN_REPLICA, mtent.dn))

          try:

              entry = self.conn.getEntry(dn_replica, ldap.SCOPE_BASE)

-             self.log.warning("Already setup replica for suffix %r", nsuffix)

+             self.log.warn("Already setup replica for suffix %r", nsuffix)

              self.conn.suffixes.setdefault(nsuffix, {})

              self.conn.replica.setProperties(replica_dn=dn_replica,

                                              properties=properties)
@@ -662,7 +664,7 @@ 

          if ents and (len(ents) > 0):

              ent = ents[0]

          elif tryrepl:

-             self.log.warning("Could not get RUV from %r entry -"

+             self.log.warn("Could not get RUV from %r entry -"

                            " trying cn=replica", suffix)

              ensuffix = escapeDNValue(normalizeDN(suffix))

              dn = ','.join(("cn=replica", "cn=%s" % ensuffix, DN_MAPPING_TREE))
@@ -1252,6 +1254,25 @@ 

  

          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

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

rebased onto b0530c3602097f8b46c58cc7bee8fe53cef256d6

5 years ago

rebased onto a18a305b715e105e1a033cce9d0914a5a20f6080

5 years ago

rebased onto 1ff482d33ae790b73310bafedf440ca95a29ea67

5 years ago

You've added the function here and in tools.jsx. But this one is unused in your code... Was it done for some future code?

In the navigation bar on the left, Database unfolds and it has suffixes bellow. Replication doesn't unfold but seems like it has the same structure (you can switch between replication suffixes).
I understand that we mostly have one replica entry on the instance but still, it looks weird... What do you think, maybe it will look more consistent if we will have the same unfolding options in the navigation bar? (for Replication - same as we have for Database now)

When I create an instance without any suffix - Monitoring tab is loading continuously so it doesn't work.

In the Logging settings we have - Maximum Log Size (in MB) = 100 - by default.
It is much more than 10000 lines (maximum that can be viewed in the UI).
Could we have paging here?
I understand that it is some amount of work (that we may postpone and create an issue for that) but I think it is necessary if we want our UI to be fully usable.

Also, please check the cockpit API for the working with files - https://cockpit-project.org/guide/149/cockpit-file.html
It has some nice features. :)

Besides that, I am really impressed!
Besides the good general usability on most of the sections, I like a lot the Replication tab's content with all of this Lag Reports, poking agreements, etc.
Great!

In the navigation bar on the left, Database unfolds and it has suffixes bellow. Replication doesn't unfold but seems like it has the same structure (you can switch between replication suffixes).
I understand that we mostly have one replica entry on the instance but still, it looks weird... What do you think, maybe it will look more consistent if we will have the same unfolding options in the navigation bar? (for Replication - same as we have for Database now)

I was going to do that, but the problem is that you can replicate random subtrees:

ou=people,o=simon.com
ou=staff, ou=employee,o=simon.com

There is no good way to link these replicated suffixes in a tree. It just becomes a list essentially, so a dropdown "list" made the most sense.

In the Logging settings we have - Maximum Log Size (in MB) = 100 - by default.
It is much more than 10000 lines (maximum that can be viewed in the UI).

Well you also don't want to load 100mb into memory. I was just "tailing the log" and not trying to use up too many resources. Typically though you would only be looking at last few lines anyway for debugging. You can also goto the terminal nav tab in Cockpit and vi the log if deeper analysis is required. But I see what you are getting at...

Could we have paging here?

Maybe, that would be nice, but right now its just a textarea so I need what option are available...

I understand that it is some amount of work (that we may postpone and create an issue for that) but I think it is necessary if we want our UI to be fully usable.

Yeah this might not be trivial, let me look into it, but we might have to push it off to "Version 2"

Also, please check the cockpit API for the working with files - https://cockpit-project.org/guide/149/cockpit-file.html
It has some nice features. :)

I will look into this! I forgot about it actually I know they have a type of file monitoring we could use use for continuously refreshing logs...

rebased onto d21a32a367ba61bb77fa99d56dc72cf26697310b

5 years ago

Changes made.

I did not add pagination to the log viewing. It is NOT trivial to add, and not that important IMHO. It would be nice to have, and we look into doing it, but it can wait. In the meantime I added a 50000 line option which should be plenty for most cases.

I think it will throw an exception because basedn is not specified. Check lib389/tombstone.py

Besides that, looks good. I am not sure if somebody else from our team would like to check the functionality but you have my ack.

I think it will throw an exception because basedn is not specified. Check lib389/tombstone.py

Good to know, but I actually don't use this function in the UI at this time, but I fix it up for future use.

rebased onto ab94fc1

5 years ago

Pull-Request has been merged by mreynolds

5 years ago

389-ds-base is moving from Pagure to Github. This means that new issues and pull requests
will be accepted only in 389-ds-base's github repository.

This pull request has been cloned to Github as issue and is available here:
- https://github.com/389ds/389-ds-base/issues/3382

If you want to continue to work on the PR, please navigate to the github issue,
download the patch from the attachments and file a new pull request.

Thank you for understanding. We apologize for all inconvenience.

Pull-Request has been closed by spichugi

3 years ago
Metadata
Changes Summary 46
+68 -30
file changed
src/cockpit/389-console/src/css/ds.css
+15 -8
file changed
src/cockpit/389-console/src/database.jsx
+44 -8
file changed
src/cockpit/389-console/src/ds.js
+10 -3
file changed
src/cockpit/389-console/src/index.es6
+9 -25
file changed
src/cockpit/389-console/src/index.html
+4 -2
file changed
src/cockpit/389-console/src/lib/database/attrEncryption.jsx
+12 -6
file changed
src/cockpit/389-console/src/lib/database/backups.jsx
+16 -9
file changed
src/cockpit/389-console/src/lib/database/chaining.jsx
+3 -2
file changed
src/cockpit/389-console/src/lib/database/databaseConfig.jsx
+12 -7
file changed
src/cockpit/389-console/src/lib/database/indexes.jsx
+4 -2
file changed
src/cockpit/389-console/src/lib/database/referrals.jsx
+14 -7
file changed
src/cockpit/389-console/src/lib/database/suffix.jsx
+14 -7
file changed
src/cockpit/389-console/src/lib/database/vlvIndexes.jsx
+117
file added
src/cockpit/389-console/src/lib/monitor/accesslog.jsx
+117
file added
src/cockpit/389-console/src/lib/monitor/auditfaillog.jsx
+117
file added
src/cockpit/389-console/src/lib/monitor/auditlog.jsx
+177
file added
src/cockpit/389-console/src/lib/monitor/chainingMonitor.jsx
+368
file added
src/cockpit/389-console/src/lib/monitor/dbMonitor.jsx
+140
file added
src/cockpit/389-console/src/lib/monitor/errorlog.jsx
+559
file added
src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
+1295
file added
src/cockpit/389-console/src/lib/monitor/monitorTables.jsx
+538
file added
src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
+161
file added
src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx
+223
file added
src/cockpit/389-console/src/lib/monitor/snmpMonitor.jsx
+423
file added
src/cockpit/389-console/src/lib/monitor/suffixMonitor.jsx
+1 -1
file changed
src/cockpit/389-console/src/lib/notifications.jsx
+47 -0
file changed
src/cockpit/389-console/src/lib/tools.jsx
-916
file removed
src/cockpit/389-console/src/monitor.html
-398
file removed
src/cockpit/389-console/src/monitor.js
+1187
file added
src/cockpit/389-console/src/monitor.jsx
+10 -7
file changed
src/cockpit/389-console/src/plugins.jsx
+6 -8
file changed
src/cockpit/389-console/src/replication.html
+3 -3
file changed
src/cockpit/389-console/src/replication.js
+2 -2
file changed
src/cockpit/389-console/src/schema.html
+2 -4
file changed
src/cockpit/389-console/src/servers.html
+0 -2
file changed
src/cockpit/389-console/webpack.config.js
+9 -4
file changed
src/lib389/cli/dsconf
+1 -2
file changed
src/lib389/lib389/_mapped_object.py
+53 -32
file changed
src/lib389/lib389/agreement.py
+4 -10
file changed
src/lib389/lib389/chaining.py
+4 -3
file changed
src/lib389/lib389/cli_base/__init__.py
+28 -24
file changed
src/lib389/lib389/cli_conf/backend.py
+31 -7
file changed
src/lib389/lib389/cli_conf/monitor.py
+122 -56
file changed
src/lib389/lib389/cli_conf/replication.py
+56 -27
file changed
src/lib389/lib389/monitor.py
+25 -4
file changed
src/lib389/lib389/replica.py