#50673 Issue 50592 - Port Replication Tab to ReactJS
Closed 3 years ago by spichugi. Opened 4 years ago by mreynolds.
mreynolds/389-ds-base ui  into  master

file modified
+2
@@ -246,6 +246,8 @@ 

  Requires:         perl-Mozilla-LDAP

  # for setup-ds.pl

  Requires:         bind-utils

+ %global __provides_exclude_from %{_libdir}/%{pkgname}/perl

+ %global __requires_exclude perl\\((DSCreate|DSMigration|DSUpdate|DSUtil|Dialog|DialogManager|FileConn|Inf|Migration|Resource|Setup|SetupLog)

  %{?perl_default_filter}

  %endif

  # End use perl

@@ -0,0 +1,45 @@ 

+ #!/bin/sh

+ #

+ # Do a fresh build of the UI, and run it.  While in this state all updates made

+ # are built immediately.  Just refresh the browser to test them.

+ 

+ AUDIT=0

+ while (( "$#" )); do

+     case "$1" in

+         -a|--audit)

+         AUDIT=1

+         break

+         ;;

+     -h|--help)

+         echo Usage:

+         echo This is a development script to quickly refresh the UI and watch it live

+         echo Options:

+         echo    -a|--audit    Audit the build

+         exit 0

+         ;;

+     -*|--*=)

+         echo "Error: Unsupported argument $1" >&2

+         echo "Available Options:" >&2

+         echo "   -a|--audit    Audit the build" >&2

+         exit 1

+         ;;

+     esac

+ done

+ 

+ printf "\nCleaning and installing npm packages ...\n\n"

+ make -f node_modules.mk clean > /dev/null

+ make -f node_modules.mk install > /dev/null

+ if [ $? != 0 ]; then

+     exit 1

+ fi

+ 

+ if [ $AUDIT == 1 ]; then

+     printf "\nAuditing npm packages ...\n\n"

+     make -f node_modules.mk build-cockpit-plugin

+     if [ $? != 0 ]; then

+         exit 1

+     fi

+ fi

+ 

+ printf "\nBuilding and watching ...\n"

+ node_modules/webpack/bin/webpack.js --watch

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

      vertical-align: top !important;

  }

  

+ .ds-left-align {

+     right: 0 !important;

+ }

+ 

  /* Main nav page index.html */

  .ds-content {

      padding: 0;
@@ -116,6 +120,10 @@ 

      width: 525px;

  }

  

+ .ds-no-padding {

+     padding: 0px !important;

+ }

+ 

  .ds-tree {

      font-size: 0.5;

      background-color: #f8f8f8;
@@ -131,6 +139,11 @@ 

      padding-top: 5px;

  }

  

+ .ds-disabled {

+     pointer-events: none;

+     opacity: 0.7;

+ }

+ 

  .ds-split {

      width: 400px;

  }
@@ -142,12 +155,12 @@ 

  }

  

  .ds-input-bad {

-     border-color: red;

+     border-color: red !important;

  }

  

  .ds-input-auto-bad {

      width: 100%;

-     border-color: red;

+     border-color: red !important;

      padding-left: 5px;

  }

  
@@ -238,20 +251,6 @@ 

      overflow: auto;

  }

  

- .ds-agmt-wiz-dropdown {

-     width: 175px !important;

-     text-align: left;

- }

- 

- .ds-agmt-wiz-dropdown a {

-     padding: 0px !important;

-     padding-left: 10px !important;

-     line-height: 0 !important;

-     height: 40px;

-     width: 175px !important;

-     text-align: left;

- }

- 

  .ds-dblink-dropdown {

      width: 140px !important;

      text-align: left;
@@ -594,6 +593,17 @@ 

      float: left;

  }

  

+ .ds-inline-btn {

+     margin-top: -3px;

+     margin-left: 10px;

+     display: inline-block;

+ }

+ 

+ .spinner-sm {

+     height: 18px !important;

+     Width: 18px !important;

+ }

+ 

  .ds-footer {

      background-color: #f5f5f5 !important;

      margin-left: -25px;
@@ -635,7 +645,7 @@ 

      border: 0;

      position: relative;

      overflow: hidden;

-     width: 700px;

+     width: 100%;

      text-align: left;

      font: inherit;

  }
@@ -646,7 +656,7 @@ 

      height: 1px;

      background: #228bc0;

      position: absolute;

-     width: 700px;

+     width: 100%;

      top: 50% !important;

      margin-left: 10px;

      text-align: left;
@@ -671,7 +681,7 @@ 

  }

  

  .ds-margin-top {

-     margin-top: 10px;

+     margin-top: 10px !important;

  }

  

  .ds-margin-top-med {
@@ -686,6 +696,10 @@ 

      margin-top: 35px !important;

  }

  

+ .ds-margin-top-xxlg {

+     margin-top: 45px !important;

+ }

+ 

  .ds-modal-spinner {

      margin-top: 10px;

      text-align: center;
@@ -736,6 +750,10 @@ 

      margin-left: 15px !important;

  }

  

+ .ds-left-indent-md {

+     margin-left: 30px !important;

+ }

+ 

  .ds-right-indent {

      margin-right: 15px !important;

  }
@@ -798,6 +816,10 @@ 

      color: red;

  }

  

+ .ds-clear-text {

+      opacity: 0;

+ }

+ 

  .ds-margin-left-sm {

      margin-left: 30px !important;

  }
@@ -807,7 +829,7 @@ 

  }

  

  .ds-margin-left-piechart {

-     margin-left: 130px !important;

+     margin-left: 140px !important;

  }

  

  .ds-nested-modal {
@@ -839,6 +861,13 @@ 

      transform: translate(-25%, -50%);

  }

  

+ .ds-loading-spinner-tree {

+     position: fixed;

+     top: 40%;

+     left: 40%;

+     transform: translate(-40%, -40%);

+ }

+ 

  .ds-loading {

      position: fixed;

      top: 25%;
@@ -870,6 +899,10 @@ 

      margin-left: 80px;

  }

  

+ .ds-h4 {

+     font-size: 18px !important;

+ }

+ 

  @media screen and (max-width: 1300px) {

      .ds-plugin-spinner {

          margin-left: 0px;
@@ -955,12 +988,12 @@ 

  

  .ds-word-wrap {

      word-wrap: break-word;

-     padding-top: 3px;

  }

  

  .ds-suffix-header {

-     font-size: 16px;

+     font-size: 18px;

      margin-bottom: 15px;

+     padding-top: 5px;

  }

  

  .ds-header {
@@ -1018,3 +1051,22 @@ 

  th {

      text-align: center;

  }

+ 

+ td {

+     text-align: center;

+ }

+ 

+ hr {

+     margin-top: 20px;

+     margin-bottom: 20px;

+ }

+ 

+ h3 {

+     margin-top: 20px;

+     margin-bottom: 20px;

+ }

+ 

+ h4 {

+     margin-top: 15px;

+     margin-bottom: 15px;

+ }

@@ -48,6 +48,7 @@ 

              createSuffixEntry: false,

              createSampleEntries: false,

              noSuffixInit: true,

+             disableTree: false,

  

              // DB config

              globalDBConfig: {},
@@ -83,7 +84,6 @@ 

          // Suffix

          this.showSuffixModal = this.showSuffixModal.bind(this);

          this.closeSuffixModal = this.closeSuffixModal.bind(this);

-         this.handleChange = this.handleChange.bind(this);

          this.createSuffix = this.createSuffix.bind(this);

          this.loadSuffix = this.loadSuffix.bind(this);

          this.loadSuffixConfig = this.loadSuffixConfig.bind(this);
@@ -102,6 +102,7 @@ 

  

          // Other

          this.loadSuffixTree = this.loadSuffixTree.bind(this);

+         this.enableTree = this.enableTree.bind(this);

      }

  

      componentWillMount () {
@@ -449,6 +450,13 @@ 

      }

  

      selectNode(selectedNode) {

+         if (selectedNode.selected) {

+             return;

+         }

+         this.setState({

+             disableTree: true // Disable the tree to allow node to be fully loaded

+         });

+ 

          if (selectedNode.id == "dbconfig" ||

              selectedNode.id == "chaining-config" ||

              selectedNode.id == "backups") {
@@ -976,8 +984,38 @@ 

                                                              suffixLoading: false

                                                          });

                                                      });

+                                         })

+                                         .fail(err => {

+                                             let errMsg = JSON.parse(err);

+                                             this.addNotification(

+                                                 "error",

+                                                 `Error attribute encryption for ${suffix} - ${errMsg.desc}`

+                                             );

+                                             this.setState({

+                                                 suffixLoading: false

+                                             });

                                          });

+                             })

+                             .fail(err => {

+                                 let errMsg = JSON.parse(err);

+                                 this.addNotification(

+                                     "error",

+                                     `Error loading VLV indexes for ${suffix} - ${errMsg.desc}`

+                                 );

+                                 this.setState({

+                                     suffixLoading: false

+                                 });

                              });

+                 })

+                 .fail(err => {

+                     let errMsg = JSON.parse(err);

+                     this.addNotification(

+                         "error",

+                         `Error loading config for ${suffix} - ${errMsg.desc}`

+                     );

+                     this.setState({

+                         suffixLoading: false

+                     });

                  });

      }

  
@@ -1047,9 +1085,20 @@ 

                  });

      }

  

+     enableTree () {

+         this.setState({

+             disableTree: false

+         });

+     }

+ 

      render() {

          const { nodes } = this.state;

          let db_element = "";

+         let disabled = "tree-view-container";

+         if (this.state.disableTree) {

+             disabled = "tree-view-container ds-disabled";

+         }

+ 

          if (this.state.loaded) {

              if (this.state.node_name == DB_CONFIG || this.state.node_name == "") {

                  db_element =
@@ -1058,6 +1107,7 @@ 

                          addNotification={this.addNotification}

                          reload={this.loadGlobalConfig}

                          data={this.state.globalDBConfig}

+                         enableTree={this.enableTree}

                          key={this.state.configUpdated}

                      />;

              } else if (this.state.node_name == CHAINING_CONFIG) {
@@ -1067,6 +1117,7 @@ 

                          addNotification={this.addNotification}

                          reload={this.loadChainingConfig}

                          data={this.state.chainingConfig}

+                         enableTree={this.enableTree}

                          key={this.state.chainingUpdated}

                      />;

              } else if (this.state.node_name == BACKUP_CONFIG) {
@@ -1077,6 +1128,7 @@ 

                          backups={this.state.BackupRows}

                          suffixes={this.state.suffixList}

                          ldifs={this.state.LDIFRows}

+                         enableTree={this.enableTree}

                          reload={this.loadBackups}

                      />;

              } else if (this.state.node_name != "") {
@@ -1084,10 +1136,9 @@ 

                  if (this.state.dbtype == "suffix" || this.state.dbtype == "subsuffix") {

                      if (this.state.suffixLoading) {

                          db_element =

-                             <div className="ds-loading-spinner ds-center">

-                                 <p />

+                             <div className="ds-margin-top ds-loading-spinner ds-center">

                                  <h4>Loading suffix configuration for <b>{this.state.node_text} ...</b></h4>

-                                 <Spinner loading size="md" />

+                                 <Spinner className="ds-margin-top-lg" loading size="md" />

                              </div>;

                      } else {

                          db_element =
@@ -1107,6 +1158,7 @@ 

                                  dbtype={this.state.dbtype}

                                  data={this.state[this.state.node_text]}

                                  attrs={this.state.attributes}

+                                 enableTree={this.enableTree}

                                  key={this.state.node_text}

                              />;

                      }
@@ -1114,10 +1166,9 @@ 

                      // Chaining

                      if (this.state.chainingLoading) {

                          db_element =

-                             <div className="ds-loading-spinner ds-center">

-                                 <p />

+                             <div className="ds-margin-top ds-loading-spinner ds-center">

                                  <h4>Loading chaining configuration for <b>{this.state.node_text} ...</b></h4>

-                                 <Spinner loading size="md" />

+                                 <Spinner className="ds-margin-top-lg" loading size="md" />

                              </div>;

                      } else {

                          db_element =
@@ -1128,6 +1179,7 @@ 

                                  loadSuffixTree={this.loadSuffixTree}

                                  addNotification={this.addNotification}

                                  data={this.state[this.state.node_text]}

+                                 enableTree={this.enableTree}

                                  reload={this.loadChainingLink}

                              />;

                      }
@@ -1144,7 +1196,7 @@ 

                  <div className="ds-container">

                      <div>

                          <div className="ds-tree">

-                             <div className="tree-view-container" id="db-tree"

+                             <div className={disabled} id="db-tree"

                                  style={treeViewContainerStyles}>

                                  <TreeView

                                      nodes={nodes}
@@ -1219,8 +1271,7 @@ 

                                      <input onChange={handleChange} className={error.createSuffix ? "ds-input-bad" : "ds-input"} type="text" id="createSuffix" size="40" />

                                  </Col>

                              </Row>

-                             <p />

-                             <Row title="The name for the backend database, like 'userroot'.  The name can be a combination of alphanumeric characters, dashes (-), and underscores (_). No other characters are allowed, and the name must be unique across all backends.">

+                             <Row className="ds-margin-top" title="The name for the backend database, like 'userroot'.  The name can be a combination of alphanumeric characters, dashes (-), and underscores (_). No other characters are allowed, and the name must be unique across all backends.">

                                  <Col sm={3}>

                                      <ControlLabel>Database Name</ControlLabel>

                                  </Col>

@@ -10,7 +10,7 @@ 

  var server_page_loaded = 0;

  var security_page_loaded = 1;

  var db_page_loaded = 1;

- var repl_page_loaded = 0;

+ var repl_page_loaded = 1;

  var plugin_page_loaded = 1;

  var schema_page_loaded = 0;

  var monitor_page_loaded = 1;
@@ -355,39 +355,6 @@ 

      save_config();  // Server Config Page

  }

  

- function load_repl_suffix_dropdowns() {

-   // Update replication drop downs (agmts mainly)

-   var repl_dropdowns = ['select-repl-agmt-suffix', 'select-repl-winsync-suffix',

-                         'cleanallruv-suffix', 'monitor-repl-backend-list'];

-   var repl_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'list'];

-   log_cmd('load_repl_suffix_dropdowns', 'get replicated suffix list', repl_cmd);

-   cockpit.spawn(repl_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-     // Update dropdowns

-     for (var idx in repl_dropdowns) {

-       $("#" + repl_dropdowns[idx]).find('option').remove();

-     }

-     var obj = JSON.parse(data);

-     for (var idx in obj['items']) {

-       for (var list in repl_dropdowns){

-         $("#" + repl_dropdowns[list]).append('<option value="' + obj['items'][idx] + '" selected="selected">' + obj['items'][idx] +'</option>');

-       }

-     }

-     get_and_set_repl_agmts();

-     get_and_set_repl_winsync_agmts();

-     if (obj['items'].length == 0){

-       // Disable create agmt buttons

-       $("#create-agmt").prop("disabled", true);

-       $("#winsync-create-agmt").prop("disabled", true);

-       $("#create-cleanallruv-btn").prop("disabled", true);

-     } else {

-       // Enable repl agmt buttons

-       $("#create-agmt").prop("disabled", false);

-       $("#winsync-create-agmt").prop("disabled", false);

-       $("#create-cleanallruv-btn").prop("disabled", false);

-     }

-   });

- }

- 

  var progress = 10;

  

  function update_progress () {
@@ -449,11 +416,6 @@ 

      get_and_set_schema_tables();

      update_progress();

  

-     // Replication page

-     get_and_set_repl_config();

-     get_and_set_cleanallruv();

-     update_progress();

- 

      // Initialize the tabs

      $(".ds-tab-list").css( 'color', '#777');

      $("#server-tab").css( 'color', '#228bc0');
@@ -510,4 +472,8 @@ 

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

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

    });

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

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

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

+   });

  });

@@ -4,6 +4,7 @@ 

  import { Database } from "./database.jsx";

  import { Monitor } from "./monitor.jsx";

  import { Security } from "./security.jsx";

+ import { Replication } from "./replication.jsx";

  

  var serverIdElem;

  
@@ -31,6 +32,12 @@ 

          document.getElementById("database")

      );

  

+     // Replication tab

+     ReactDOM.render(

+         <Replication serverId={serverIdElem} key={tabKey} />,

+         document.getElementById("replication")

+     );

+ 

      // Monitor tab

      ReactDOM.render(

          <Monitor serverId={serverIdElem} key={tabKey} />,

@@ -20,7 +20,6 @@ 

    <script src="ds.js"></script>

    <script src="schema.js"></script>

    <script src="servers.js"></script>

-   <script src="replication.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">
@@ -35,9 +34,9 @@ 

    <div id="reload-page" hidden></div>

    <div id="loading-page" class="ds-center ds-loading" hidden>

      <h4 id="loading-msg">Loading Directory Server Configuration...</h4>

-     <p><span class="spinner spinner-lg spinner-inline"></span></p>

+     <p class="ds-margin-top-lg"><span class="spinner spinner-lg spinner-inline"></span></p>

  

-     <div class="progress">

+     <div class="progress ds-margin-top-lg">

          <div class="progress-bar" role="progressbar" id="ds-progress-bar"

              aria-valuenow="20" aria-valuemin="0"

              aria-valuemax="100" style="width: 20%;"
@@ -46,7 +45,6 @@ 

          </div>

      </div>

  

- 

    </div>

    <div id="everything" hidden>

      <div class="ds-nav-bar">
@@ -103,17 +101,10 @@ 

              </li>

  

              <!-- Replication navtab -->

-             <li class="dropdown ds-nav-tab">

-               <a href="#0" class="ds-tab-list" data-toggle="dropdown" id="replication-tab">

-                 Replication

-                 <b class="caret"></b>

-               </a>

-               <ul class="dropdown-menu ds-nav-item">

-                 <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-tasks-btn" parent-id="replication-tab">Replication Tasks</a></li>

-               </ul>

+             <li class="ds-nav-tab">

+                 <a href="#0" class="ds-tab-list ds-tab-standalone" id="replication-tab">

+                     Replication

+                 </a>

              </li>

  

              <!-- Schema navtab -->
@@ -502,6 +493,12 @@ 

        </div>

  

        <div id="server-content" class="all-pages" hidden>

+           <div id="server-settings"></div>

+           <div id="server-tuning"></div>

+           <div id="server-sasl"></div>

+           <div id="server-pwp"></div>

+           <div id="server-ldapi"></div>

+           <div id="server-logs"></div>

        </div>

  

        <div id="security-content" class="all-pages" hidden>
@@ -513,6 +510,7 @@ 

        </div>

  

        <div id="replication-content" class="all-pages" hidden>

+           <div id="replication"></div>

        </div>

  

        <div id="schema-content" class="all-pages" hidden>

@@ -129,8 +129,7 @@ 

                      rows={this.props.rows}

                      loadModalHandler={this.showConfirmAttrDelete}

                  />

-                 <p />

-                 <Row>

+                 <Row className="ds-margin-top">

                      <Col sm={6}>

                          <Typeahead

                              id="attrEncrypt"

@@ -8,6 +8,7 @@ 

      TabContent,

      TabPane,

      TabContainer,

+     Checkbox,

      Col,

      Button,

      Spinner,
@@ -46,6 +47,7 @@ 

              showLDIFDeleteSpinningModal: false,

              showExportModal: false,

              exportSpinner: false,

+             includeReplData: false,

              ldifName: "",

              ldifSuffix: "",

              errObj: {}
@@ -88,12 +90,17 @@ 

          this.closeConfirmLDIFReplace = this.closeConfirmLDIFReplace.bind(this);

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      showExportModal () {

          this.setState({

              showExportModal: true,

              exportSpinner: false,

              ldifName: "",

-             ldifSuffix: this.props.suffixes[0]

+             ldifSuffix: this.props.suffixes[0],

+             includeReplData: false,

          });

      }

  
@@ -254,7 +261,7 @@ 

      importLDIF() {

          this.showLDIFSpinningModal();

  

-         const cmd = [

+         let cmd = [

              "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",

              "backend", "import", this.state.ldifSuffix, this.state.ldifName, "--encrypted"

          ];
@@ -281,7 +288,7 @@ 

      deleteLDIF (e) {

          this.showLDIFDeleteSpinningModal();

  

-         const cmd = [

+         let cmd = [

              "dsctl", this.props.serverId, "ldifs", "--delete", this.state.ldifName

          ];

          log_cmd("deleteLDIF", "Deleting LDIF", cmd);
@@ -491,6 +498,10 @@ 

              "backend", "export", this.state.ldifSuffix, "--encrypted", "--ldif=" + this.state.ldifName

          ];

  

+         if (this.state.includeReplData) {

+             export_cmd.push("--replication");

+         }

+ 

          this.setState({

              exportSpinner: true,

          });
@@ -545,13 +556,22 @@ 

                                          confirmDelete={this.showConfirmBackupDelete}

                                      />

                                  </div>

-                                 <p />

-                                 <Button

-                                     bsStyle="primary"

-                                     onClick={this.showBackupModal}

-                                 >

-                                     Create Backup

-                                 </Button>

+                                 <div className="ds-inline">

+                                     <Button

+                                         bsStyle="primary"

+                                         onClick={this.showBackupModal}

+                                         className="ds-margin-top"

+                                     >

+                                         Create Backup

+                                     </Button>

+                                     <Button

+                                         bsStyle="default"

+                                         onClick={this.props.reload}

+                                         className="ds-left-margin ds-margin-top"

+                                     >

+                                         Refresh Backups

+                                     </Button>

+                                 </div>

                              </TabPane>

  

                              <TabPane eventKey={2}>
@@ -562,13 +582,22 @@ 

                                          confirmDelete={this.showConfirmLDIFDelete}

                                      />

                                  </div>

-                                 <p />

-                                 <Button

-                                     bsStyle="primary"

-                                     onClick={this.showExportModal}

-                                 >

-                                     Create LDIF Export

-                                 </Button>

+                                 <div className="ds-inline">

+                                     <Button

+                                         bsStyle="primary"

+                                         onClick={this.showExportModal}

+                                         className="ds-margin-top"

+                                     >

+                                         Create LDIF Export

+                                     </Button>

+                                     <Button

+                                         bsStyle="default"

+                                         onClick={this.props.reload}

+                                         className="ds-left-margin ds-margin-top"

+                                     >

+                                         Refresh LDIFs

+                                     </Button>

+                                 </div>

                              </TabPane>

                          </TabContent>

                      </div>
@@ -677,7 +706,7 @@ 

          if (spinning) {

              spinner =

                  <Row>

-                     <div className="ds-modal-spinner">

+                     <div className="ds-margin-top ds-modal-spinner">

                          <Spinner loading inline size="md" />Exporting database... <font size="2">(You can safely close this window)</font>

                      </div>

                  </Row>;
@@ -713,8 +742,7 @@ 

                                      </select>

                                  </Col>

                              </Row>

-                             <p />

-                             <Row title="Name of exported LDIF file, if left blank the data and time will be used as the file name">

+                             <Row className="ds-margin-top" title="Name of exported LDIF file, if left blank the data and time will be used as the file name">

                                  <Col sm={3}>

                                      <ControlLabel>LDIF File Name</ControlLabel>

                                  </Col>
@@ -727,7 +755,17 @@ 

                                      />

                                  </Col>

                              </Row>

-                             <p />

+                             <Row className="ds-margin-top-xlg">

+                                 <Col sm={12} className="ds-margin-left">

+                                     <Checkbox

+                                         id="includeReplData"

+                                         onChange={handleChange}

+                                         title="Include the replication metadata needed to restore or initialize another replica."

+                                     >

+                                         Include Replication Data

+                                     </Checkbox>

+                                 </Col>

+                             </Row>

                              {spinner}

                          </Form>

                      </Modal.Body>
@@ -766,7 +804,7 @@ 

          if (spinning) {

              spinner =

                  <Row>

-                     <div className="ds-modal-spinner">

+                     <div className="ds-margin-top ds-modal-spinner">

                          <Spinner loading inline size="md" />Backing up databases... <font size="2">(You can safely close this window)</font>

                      </div>

                  </Row>;
@@ -803,7 +841,6 @@ 

                                      />

                                  </Col>

                              </Row>

-                             <p />

                              {spinner}

                          </Form>

                      </Modal.Body>
@@ -856,8 +893,7 @@ 

                          <Form horizontal autoComplete="off">

                              <div className="ds-modal-spinner">

                                  <Spinner loading inline size="md" /> Restoring backup <b>{msg}</b> ...

-                                 <p />

-                                 <p><font size="2"> (You can safely close this window)</font></p>

+                                 <p className="ds-margin-top"><font size="2"> (You can safely close this window)</font></p>

                              </div>

                          </Form>

                      </Modal.Body>
@@ -904,8 +940,7 @@ 

                          <Form horizontal autoComplete="off">

                              <div className="ds-modal-spinner">

                                  <Spinner loading inline size="md" /> Deleting backup <b>{msg}</b> ...

-                                 <p />

-                                 <p><font size="2"> (You can safely close this window)</font></p>

+                                 <p className="ds-margin-top"><font size="2"> (You can safely close this window)</font></p>

                              </div>

                          </Form>

                      </Modal.Body>
@@ -952,8 +987,7 @@ 

                          <Form horizontal autoComplete="off">

                              <div className="ds-modal-spinner">

                                  <Spinner loading inline size="md" /> Importing LDIF <b>{msg}</b> ...

-                                 <p />

-                                 <p><font size="2"> (You can safely close this window)</font></p>

+                                 <p className="ds-margin-top"><font size="2"> (You can safely close this window)</font></p>

                              </div>

                          </Form>

                      </Modal.Body>
@@ -1000,8 +1034,7 @@ 

                          <Form horizontal autoComplete="off">

                              <div className="ds-modal-spinner">

                                  <Spinner loading inline size="md" /> Deleting LDIF file <b>{msg}</b> ...

-                                 <p />

-                                 <p><font size="2"> (You can safely close this window)</font></p>

+                                 <p className="ds-margin-top"><font size="2"> (You can safely close this window)</font></p>

                              </div>

                          </Form>

                      </Modal.Body>
@@ -1068,11 +1101,13 @@ 

  Backups.propTypes = {

      backups: PropTypes.array,

      ldifs: PropTypes.array,

-     reload: PropTypes.func

+     reload: PropTypes.func,

+     enableTree: PropTypes.func,

  };

  

  Backups.defaultProps = {

      backups: [],

      ldifs: [],

-     reload: noop

+     reload: noop,

+     enableTree: noop,

  };

@@ -1,6 +1,6 @@ 

  import cockpit from "cockpit";

  import React from "react";

- import { ConfirmPopup } from "../notifications.jsx";

+ import { ConfirmPopup, DoubleConfirmModal } from "../notifications.jsx";

  import CustomCollapse from "../customCollapse.jsx";

  import { log_cmd } from "../tools.jsx";

  import {
@@ -11,7 +11,6 @@ 

      Row,

      Col,

      ControlLabel,

-     Checkbox,

      FormControl,

      noop

  } from "patternfly-react";
@@ -98,6 +97,10 @@ 

          this.closeConfirmCompDelete = this.closeConfirmCompDelete.bind(this);

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      handleChange(e) {

          // Generic

          const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
@@ -454,7 +457,7 @@ 

          );

  

          return (

-             <div id="db-global-page">

+             <div id="chaining-page">

                  <h3 className="ds-config-header">Database Chaining Settings</h3>

                  <hr />

                  <div className="ds-container">
@@ -492,66 +495,143 @@ 

                          </div>

                      </div>

                  </div>

- 

-                 <h4 className="ds-sub-header"><br />Default Database Link Creation Settings</h4>

+                 <h4 className="ds-margin-top ds-sub-header ds-center">Default Database Link Creation Settings</h4>

                  <hr />

-                 <div className="ds-container">

-                     <div className="ds-split">

-                         <label htmlFor="defSizeLimit" className="ds-config-label" title="The size limit of entries returned over a database link (nsslapd-sizelimit).">

-                             Size Limit</label><input className="ds-input" type="text" id="defSizeLimit" onChange={this.handleChange} value={this.state.defSizeLimit} size="15" />

-                         <label htmlFor="defTimeLimit" className="ds-config-label" title="The time limit of an operation over a database link (nsslapd-timelimit).">

-                             Time Limit</label><input className="ds-input" type="text" id="defTimeLimit" onChange={this.handleChange} value={this.state.defTimeLimit} size="15" />

-                         <label htmlFor="defBindConnLimit" className="ds-config-label" title="The maximum number of TCP connections the database link establishes with the remote server.  (nsbindconnectionslimit).">

-                             Max TCP Connections</label><input className="ds-input" id="defBindConnLimit" type="text" onChange={this.handleChange} value={this.state.defBindConnLimit} size="15" />

-                         <label htmlFor="defOpConnLimit" className="ds-config-label" title="The maximum number of connections allowed over the database link.  (nsoperationconnectionslimit).">

-                             Max LDAP Connections</label><input className="ds-input" id="defOpConnLimit" type="text" onChange={this.handleChange} value={this.state.defOpConnLimit} size="15" />

-                         <label htmlFor="defConcurLimit" className="ds-config-label" title="The maximum number of concurrent bind operations per TCP connection. (nsconcurrentbindlimit).">

-                             Max Binds Per Connection</label><input className="ds-input" id="defConcurLimit" type="text" onChange={this.handleChange} value={this.state.defConcurLimit} size="15" />

-                         <label htmlFor="defBindTimeout" className="ds-config-label" title="The amount of time before the bind attempt times out. (nsbindtimeout).">

-                             Bind Timeout</label><input className="ds-input" id="defBindTimeout" type="text" onChange={this.handleChange} value={this.state.defBindTimeout} size="15" />

-                         <label htmlFor="defBindRetryLimit" className="ds-config-label" title="The number of times the database link tries to bind with the remote server after a connection failure. (nsbindretrylimit).">

-                             Bind Retry Limit</label><input className="ds-input" id="defBindRetryLimit" type="text" onChange={this.handleChange} value={this.state.defBindRetryLimit} size="15" />

-                     </div>

-                     <div className="ds-divider" />

-                     <div className="ds-split ds-inline">

-                         <div>

-                             <label htmlFor="defConcurOpLimit" className="ds-config-label" title="The maximum number of operations per connections. (nsconcurrentoperationslimit).">

-                                 Max Operations Per Connection</label><input className="ds-input" id="defConcurOpLimit" type="text" onChange={this.handleChange} value={this.state.defConcurOpLimit} size="15" />

-                         </div>

-                         <div>

-                             <label htmlFor="defConnLife" className="ds-config-label" title="The life of a database link connection to the remote server.  0 is unlimited  (nsconnectionlife).">

-                                 Connection Lifetime (in seconds)</label><input className="ds-input" id="defConnLife" type="text" onChange={this.handleChange} value={this.state.defConnLife} size="15" />

-                         </div>

-                         <div>

-                             <label htmlFor="defSearchCheck" className="ds-config-label" title="The number of seconds that pass before the server checks for abandoned operations.  (nsabandonedsearchcheckinterval).">

-                                 Abandoned Op Check Interval</label><input className="ds-input" id="defSearchCheck" type="text" onChange={this.handleChange} value={this.state.defSearchCheck} size="15" />

-                         </div>

-                         <div>

-                             <label htmlFor="defHopLimit" className="ds-config-label" title="The maximum number of times a request can be forwarded from one database link to another.  (nshoplimit).">

-                                 Database Link Hop Limit</label><input className="ds-input" type="text" onChange={this.handleChange} value={this.state.defHopLimit} id="defHopLimit" size="15" />

-                         </div>

-                         <div>

-                             <p />

-                             <input type="checkbox" onChange={this.handleChange} checked={this.state.defCheckAci} className="ds-config-checkbox" id="defCheckAci" /><label

-                                 htmlFor="defCheckAci" className="ds-label" title="Sets whether ACIs are evaluated on the database link as well as the remote data server (nschecklocalaci)."> Check Local ACIs</label>

-                         </div>

-                         <div>

-                             <input type="checkbox" onChange={this.handleChange} checked={this.state.defRefOnScoped} className="ds-config-checkbox" id="defRefOnScoped" /><label

-                                 htmlFor="defRefOnScoped" className="ds-label" title="Sets whether referrals are returned by scoped searches (meaning 'one-level' or 'subtree' scoped searches). (nsreferralonscopedsearch)."> Send Referral On Scoped Search</label>

-                         </div>

-                         <div>

-                             <input type="checkbox" onChange={this.handleChange} checked={this.state.defProxy} className="ds-config-checkbox" id="defProxy" /><label

-                                 htmlFor="defProxy" className="ds-label" title="Sets whether proxied authentication is allowed (nsproxiedauthorization)."> Allow Proxied Authentication</label>

-                         </div>

-                         <div>

-                             <input type="checkbox" onChange={this.handleChange} checked={this.state.defUseStartTLS} className="ds-config-checkbox" id="defUseStartTLS" /><label

-                                 htmlFor="defUseStartTLS" className="ds-label" title="Sets whether to use Start TLS to initiate a secure, encrypted connection over an insecure port.  (nsusestarttls)."> Use StartTLS</label>

-                         </div>

-                     </div>

-                 </div>

-                 <div className="ds-margin-top-lg">

-                     <button className="btn btn-primary save-button" onClick={this.save_chaining_config}>Save Default Settings</button>

-                 </div>

+                 <Form horizontal>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The size limit of entries returned over a database link (nsslapd-sizelimit).">

+                             Size Limit

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defSizeLimit" onChange={this.handleChange} defaultValue={this.state.defSizeLimit} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The maximum number of operations per connections. (nsconcurrentoperationslimit).">

+                             Max Operations Per Conn

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defConcurOpLimit" onChange={this.handleChange} defaultValue={this.state.defConcurOpLimit} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The time limit of an operation over a database link (nsslapd-timelimit).">

+                             Time Limit

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defTimeLimit" onChange={this.handleChange} defaultValue={this.state.defTimeLimit} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The maximum number of operations per connections. (nsconcurrentoperationslimit).">

+                             Connection Lifetime

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defConnLife" onChange={this.handleChange} defaultValue={this.state.defConnLife} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The maximum number of TCP connections the database link establishes with the remote server.  (nsbindconnectionslimit).">

+                             Max TCP Connections

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defBindConnLimit" onChange={this.handleChange} defaultValue={this.state.defBindConnLimit} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The number of seconds that pass before the server checks for abandoned operations.  (nsabandonedsearchcheckinterval).">

+                             Abandoned Op Check Interval

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defSearchCheck" onChange={this.handleChange} defaultValue={this.state.defSearchCheck} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The maximum number of connections allowed over the database link.  (nsoperationconnectionslimit).">

+                             Max LDAP Connections

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defOpConnLimit" onChange={this.handleChange} defaultValue={this.state.defOpConnLimit} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The number of seconds that pass before the server checks for abandoned operations.  (nsabandonedsearchcheckinterval).">

+                             Abandoned Op Check Interval

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defSearchCheck" onChange={this.handleChange} defaultValue={this.state.defSearchCheck} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The maximum number of connections allowed over the database link.  (nsoperationconnectionslimit).">

+                             Max Binds Per Connection

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defOpConnLimit" onChange={this.handleChange} defaultValue={this.state.defOpConnLimit} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The maximum number of times a request can be forwarded from one database link to another.  (nshoplimit).">

+                             Database Link Hop Limit

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defHopLimit" onChange={this.handleChange} defaultValue={this.state.defHopLimit} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The amount of time before the bind attempt times out. (nsbindtimeout).">

+                             Bind Timeout

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defBindTimeout" onChange={this.handleChange} defaultValue={this.state.defBindTimeout} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="The number of times the database link tries to bind with the remote server after a connection failure. (nsbindretrylimit).">

+                             Bind Retry Limit

+                         </Col>

+                         <Col sm={8}>

+                             <input className="ds-input-auto" type="text" id="defBindRetryLimit" onChange={this.handleChange} defaultValue={this.state.defBindRetryLimit} />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="Sets whether ACIs are evaluated on the database link as well as the remote data server (nschecklocalaci).">

+                             Check Local ACIs

+                         </Col>

+                         <Col sm={8}>

+                             <input type="checkbox" onChange={this.handleChange} defaultChecked={this.state.defCheckAci} className="ds-config-checkbox" id="nsusdefCheckAciestarttls" />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="Sets whether referrals are returned by scoped searches (meaning 'one-level' or 'subtree' scoped searches). (nsreferralonscopedsearch).">

+                             Send Referral On Scoped Search

+                         </Col>

+                         <Col sm={8}>

+                             <input type="checkbox" onChange={this.handleChange} defaultChecked={this.state.defRefOnScoped} className="ds-config-checkbox" id="defRefOnScoped" />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="Sets whether ACIs are evaluated on the database link as well as the remote data server (nschecklocalaci).">

+                             Allow Proxied Authentication

+                         </Col>

+                         <Col sm={8}>

+                             <input type="checkbox" onChange={this.handleChange} defaultChecked={this.state.defProxy} className="ds-config-checkbox" id="defProxy" />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top">

+                         <Col componentClass={ControlLabel} sm={4} title="Sets whether referrals are returned by scoped searches (meaning 'one-level' or 'subtree' scoped searches). (nsreferralonscopedsearch).">

+                             Use StartTLS

+                         </Col>

+                         <Col sm={8}>

+                             <input type="checkbox" onChange={this.handleChange} defaultChecked={this.state.defUseStartTLS} className="ds-config-checkbox" id="defUseStartTLS" />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top-lg">

+                         <Col sm={5}>

+                             <button className="btn btn-primary save-button" onClick={this.save_chaining_config}>Save Default Settings</button>

+                         </Col>

+                     </Row>

+                 </Form>

  

                  <ChainControlsModal

                      showModal={this.state.showOidModal}
@@ -598,6 +678,8 @@ 

                  errObj: {},

                  showDeleteConfirm: false,

                  linkPwdMatch: true,

+                 modalSpinning: false,

+                 modalChecked: false,

                  // Settings

                  nsfarmserverurl: this.props.data.nsfarmserverurl,

                  nsmultiplexorbinddn: this.props.data.nsmultiplexorbinddn,
@@ -655,15 +737,23 @@ 

          this.closeDeleteConfirm = this.closeDeleteConfirm.bind(this);

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      showDeleteConfirm () {

          this.setState({

-             showDeleteConfirm: true

+             showDeleteConfirm: true,

+             modalSpinning: false,

+             modalChecked: false,

          });

      }

  

      closeDeleteConfirm () {

          this.setState({

-             showDeleteConfirm: false

+             showDeleteConfirm: false,

+             modalSpinning: false,

+             modalChecked: false,

          });

      }

  
@@ -874,89 +964,26 @@ 

  

      render () {

          const error = this.state.errObj;

-         let useStartTLSCheckBox;

-         let checkLocalAci;

-         let referralOnScope;

-         let proxiedAuth;

- 

-         // Check local aci's checkbox

-         if (this.state.nschecklocalaci) {

-             checkLocalAci =

-                 <Checkbox id="nschecklocalaci" onChange={this.handleChange} key={this.state.nschecklocalaci} defaultChecked

-                     title="Sets whether ACIs are evaluated on the database link as well as the remote data server (nschecklocalaci).">

-                     Check Local ACIs

-                 </Checkbox>;

-         } else {

-             checkLocalAci =

-                 <Checkbox id="nschecklocalaci" onChange={this.handleChange} key={this.state.nschecklocalaci}

-                     title="Sets whether ACIs are evaluated on the database link as well as the remote data server (nschecklocalaci).">

-                     Check Local ACIs

-                 </Checkbox>;

-         }

-         // Referral on scoped search checkbox

-         if (this.state.nsreferralonscopedsearch) {

-             referralOnScope =

-                 <Checkbox id="nsreferralonscopedsearch" onChange={this.handleChange} defaultChecked

-                     title="Sets whether referrals are returned by scoped searches (meaning 'one-level' or 'subtree' scoped searches). (nsreferralonscopedsearch).">

-                     Send Referral On Scoped Search

-                 </Checkbox>;

-         } else {

-             referralOnScope =

-                 <Checkbox id="nsreferralonscopedsearch" onChange={this.handleChange}

-                     title="Sets whether referrals are returned by scoped searches (meaning 'one-level' or 'subtree' scoped searches). (nsreferralonscopedsearch).">

-                     Send Referral On Scoped Search

-                 </Checkbox>;

-         }

-         // Allow proxied auth checkbox

-         if (this.state.nsproxiedauthorization) {

-             proxiedAuth =

-                 <Checkbox id="nsproxiedauthorization" onChange={this.handleChange} defaultChecked

-                     title="Allow proxied authentication to the remote server. (nsproxiedauthorization).">

-                     Allow Proxied Authentication

-                 </Checkbox>;

-         } else {

-             proxiedAuth =

-                 <Checkbox id="nsproxiedauthorization" onChange={this.handleChange}

-                     title="Allow proxied authentication to the remote server. (nsproxiedauthorization).">

-                     Allow Proxied Authentication

-                 </Checkbox>;

-         }

-         // use startTLS checkbox

-         if (this.state.nsusestarttls) {

-             useStartTLSCheckBox =

-                 <Checkbox id="nsusestarttls" onChange={this.handleChange} title="Use StartTLS for connection to remote server. (nsusestarttls)"

-                     defaultChecked

-                 >

-                     Use StartTLS for remote connection

-                 </Checkbox>;

-         } else {

-             useStartTLSCheckBox =

-                 <Checkbox id="nsusestarttls" onChange={this.handleChange} title="Use StartTLS for connection to remote server. (nsusestarttls)">

-                     Use StartTLS for remote connection

-                 </Checkbox>;

-         }

  

          return (

-             <div className="container-fluid">

+             <div>

                  <Row>

-                     <Col sm={8} className="ds-word-wrap">

+                     <Col sm={10} className="ds-word-wrap">

                          <ControlLabel className="ds-suffix-header"><Icon type="fa" name="link" /> <b>{this.props.suffix}</b> (<i>{this.props.bename}</i>)</ControlLabel>

                      </Col>

                      <Col sm={2}>

                          <Button

-                             bsStyle="primary"

+                             bsStyle="danger"

                              onClick={this.showDeleteConfirm}

                          >

                              Delete Link

                          </Button>

                      </Col>

                  </Row>

-                 <h4>Database Link Configuration</h4>

-                 <hr />

-                 <Form horizontal autoComplete="off">

+                 <Form horizontal autoComplete="off" className="ds-margin-top-xlg">

                      <Row title="The LDAP URL for the remote server.  Add additional failure server URLs by separating them with a space. (nsfarmserverurl)">

-                         <Col sm={4}>

-                             <ControlLabel>Remote Server LDAP URL</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={4}>

+                             Remote Server LDAP URL

                          </Col>

                          <Col sm={8}>

                              <FormControl
@@ -968,10 +995,9 @@ 

                              />

                          </Col>

                      </Row>

-                     <p />

-                     <Row title="The distinguished name (DN) of the entry to authenticate to the remote server. (nsmultiplexorbinddn)">

-                         <Col sm={4}>

-                             <ControlLabel>Remote Server Bind DN</ControlLabel>

+                     <Row className="ds-margin-top" title="The distinguished name (DN) of the entry to authenticate to the remote server. (nsmultiplexorbinddn)">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             Remote Server Bind DN

                          </Col>

                          <Col sm={8}>

                              <FormControl
@@ -983,10 +1009,9 @@ 

                              />

                          </Col>

                      </Row>

-                     <p />

-                     <Row title="The password for the authenticating entry. (nsmultiplexorcredentials)">

-                         <Col sm={4}>

-                             <ControlLabel>Bind DN Password</ControlLabel>

+                     <Row className="ds-margin-top" title="The password for the authenticating entry. (nsmultiplexorcredentials)">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             Bind DN Password

                          </Col>

                          <Col sm={8}>

                              <FormControl
@@ -998,10 +1023,9 @@ 

                              />

                          </Col>

                      </Row>

-                     <p />

-                     <Row title="Confirm the password for the authenticating entry. (nsmultiplexorcredentials)">

-                         <Col sm={4}>

-                             <ControlLabel>Confirm Password</ControlLabel>

+                     <Row className="ds-margin-top" title="Confirm the password for the authenticating entry. (nsmultiplexorcredentials)">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             Confirm Password

                          </Col>

                          <Col sm={8}>

                              <FormControl
@@ -1013,10 +1037,9 @@ 

                              />

                          </Col>

                      </Row>

-                     <p />

-                     <Row title="The authentication mechanism.  Simple (user name and password), SASL/DIGEST-MD5, or SASL>GSSAPI. (nsbindmechanism)">

-                         <Col sm={4}>

-                             <ControlLabel>Bind Mechanism</ControlLabel>

+                     <Row className="ds-margin-top" title="The authentication mechanism.  Simple (user name and password), SASL/DIGEST-MD5, or SASL>GSSAPI. (nsbindmechanism)">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             Bind Mechanism

                          </Col>

                          <Col sm={8}>

                              <select value={this.state.nsbindmechanism}
@@ -1030,87 +1053,214 @@ 

                              </select>

                          </Col>

                      </Row>

-                     <p />

-                     <Row>

-                         <Col sm={9}>

-                             {useStartTLSCheckBox}

+                     <Row className="ds-margin-top" title="Use StartTLS for connections to the remote server. (nsusestarttls)">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             Use StartTLS

+                         </Col>

+                         <Col sm={8}>

+                             <input type="checkbox" onChange={this.props.handleChange} defaultChecked={this.state.nsusestarttls} className="ds-config-checkbox" id="nsusestarttls" />

                          </Col>

                      </Row>

                  </Form>

-                 <p />

  

-                 <CustomCollapse>

-                     <div className="ds-margin-top">

-                         <div className="ds-margin-left">

-                             <div>

-                                 <label htmlFor="sizelimit" className="ds-config-label" title="The size limit of entries returned over a database link (nsslapd-sizelimit).">

-                                     Size Limit</label><input onChange={this.handleChange} defaultValue={this.state.sizelimit} className="ds-input" type="text" id="sizelimit" size="15" />

-                             </div>

-                             <div>

-                                 <label htmlFor="timelimit" className="ds-config-label" title="The time limit of an operation over a database link (nsslapd-timelimit).">

-                                     Time Limit</label><input onChange={this.handleChange} defaultValue={this.state.timelimit} className="ds-input" type="text" id="timelimit" size="15" />

-                             </div>

-                             <div>

-                                 <label htmlFor="bindconnlimit" className="ds-config-label" title="The maximum number of TCP connections the database link establishes with the remote server.  (nsbindconnectionslimit).">

-                                     Max TCP Connections</label><input onChange={this.handleChange} defaultValue={this.state.bindconnlimit} className="ds-input" type="text" id="bindconnlimit" size="15" />

-                             </div>

-                             <div>

-                                 <label htmlFor="opconnlimit" className="ds-config-label" title="The maximum number of connections allowed over the database link.  (nsoperationconnectionslimit).">

-                                     Max LDAP Connections</label><input onChange={this.handleChange} defaultValue={this.state.opconnlimit} className="ds-input" type="text" id="opconnlimit" size="15" />

-                             </div>

-                             <div>

-                                 <label htmlFor="concurrbindlimit" className="ds-config-label" title="The maximum number of concurrent bind operations per TCP connection. (nsconcurrentbindlimit).">

-                                     Max Binds Per Connection</label><input onChange={this.handleChange} defaultValue={this.state.concurrbindlimit} className="ds-input" type="text" id="concurrbindlimit" size="15" />

-                             </div>

-                             <div>

-                                 <label htmlFor="bindtimeout" className="ds-config-label" title="The amount of time before the bind attempt times out. (nsbindtimeout).">

-                                     Bind Timeout</label><input onChange={this.handleChange} defaultValue={this.state.bindtimeout} className="ds-input" type="text" id="bindtimeout" size="15" />

-                             </div>

- 

-                             <div>

-                                 <label htmlFor="bindretrylimit" className="ds-config-label" title="The number of times the database link tries to bind with the remote server after a connection failure. (nsbindretrylimit).">

-                                     Bind Retry Limit</label><input onChange={this.handleChange} defaultValue={this.state.bindretrylimit} className="ds-input" type="text" id="bindretrylimit" size="15" />

-                             </div>

-                             <div>

-                                 <label htmlFor="concurroplimit" className="ds-config-label" title="The maximum number of operations per connections. (nsconcurrentoperationslimit).">

-                                     Max Operations Per Connection</label><input onChange={this.handleChange} defaultValue={this.state.concurroplimit} className="ds-input" type="text" id="concurroplimit" size="15" />

-                             </div>

-                             <div>

-                                 <label htmlFor="connlifetime" className="ds-config-label" title="The life of a database link connection to the remote server.  0 is unlimited  (nsconnectionlife).">

-                                     Connection Lifetime (in seconds)</label><input onChange={this.handleChange} defaultValue={this.state.connlifetime} className="ds-input" type="text" id="connlifetime" size="15" />

-                             </div>

-                             <div>

-                                 <label htmlFor="searchcheckinterval" className="ds-config-label" title="The number of seconds that pass before the server checks for abandoned operations.  (nsabandonedsearchcheckinterval).">

-                                     Abandoned Op Check Interval</label><input onChange={this.handleChange} defaultValue={this.state.searchcheckinterval} className="ds-input" type="text" id="searchcheckinterval" size="15" />

-                             </div>

-                             <div>

-                                 <label htmlFor="hoplimit" className="ds-config-label" title="The maximum number of times a request can be forwarded from one database link to another.  (nshoplimit).">

-                                     Database Link Hop Limit</label><input onChange={this.handleChange} defaultValue={this.state.hoplimit} className="ds-input" type="text" id="hoplimit" size="15" />

-                             </div>

-                             <p />

-                             <div>

-                                 {proxiedAuth}

-                             </div>

-                             <div>

-                                 {checkLocalAci}

-                             </div>

-                             <div>

-                                 {referralOnScope}

-                             </div>

-                         </div>

-                     </div>

+                 <CustomCollapse className="ds-margin-top">

+                     <Form horizontal className="ds-margin-top ds-margin-left">

+                         <Row className="ds-margin-top" title="The size limit of entries returned over a database link (nsslapd-sizelimit).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Size Limit

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="sizelimit"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.sizelimit}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The time limit of an operation over a database link (nsslapd-timelimit).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Time Limit

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="timelimit"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.timelimit}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The maximum number of TCP connections the database link establishes with the remote server.  (nsbindconnectionslimit).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Max TCP Connections

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="bindconnlimit"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.bindconnlimit}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The maximum number of connections allowed over the database link.  (nsoperationconnectionslimit).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Max LDAP Connections

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="opconnlimit"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.opconnlimit}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The maximum number of concurrent bind operations per TCP connection. (nsconcurrentbindlimit).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Max Binds Per Connection

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="concurrbindlimit"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.concurrbindlimit}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The amount of time before the bind attempt times out. (nsbindtimeout).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Bind Timeout

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="bindtimeout"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.bindtimeout}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The number of times the database link tries to bind with the remote server after a connection failure. (nsbindretrylimit).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Bind Retry Limit

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="bindretrylimit"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.bindretrylimit}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The maximum number of operations per connections. (nsconcurrentoperationslimit).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Max Operations Per Connection

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="concurroplimit"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.concurroplimit}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The life of a database link connection to the remote server in seconds.  0 is unlimited  (nsconnectionlife).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Connection Lifetime

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="connlifetime"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.connlifetime}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The number of seconds that pass before the server checks for abandoned operations.  (nsabandonedsearchcheckinterval).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Abandoned Op Check Interval

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="searchcheckinterval"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.searchcheckinterval}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The maximum number of times a request can be forwarded from one database link to another.  (nshoplimit).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Hop Limit

+                             </Col>

+                             <Col sm={8}>

+                                 <FormControl

+                                     type="text"

+                                     id="hoplimit"

+                                     className="ds-input-auto"

+                                     onChange={this.handleChange}

+                                     defaultValue={this.state.hoplimit}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="Allow proxied authentication to the remote server. (nsproxiedauthorization).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Allow Proxied Authentication

+                             </Col>

+                             <Col sm={8}>

+                                 <input type="checkbox" onChange={this.props.handleChange} defaultChecked={this.state.nsproxiedauthorization} className="ds-config-checkbox" id="nsproxiedauthorization" />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="Sets whether ACIs are evaluated on the database link as well as the remote data server (nschecklocalaci).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Check Local ACIs

+                             </Col>

+                             <Col sm={8}>

+                                 <input type="checkbox" onChange={this.props.handleChange} defaultChecked={this.state.nschecklocalaci} className="ds-config-checkbox" id="nschecklocalaci" />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="Sets whether referrals are returned by scoped searches (meaning 'one-level' or 'subtree' scoped searches). (nsreferralonscopedsearch).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Send Referral On Scoped Search

+                             </Col>

+                             <Col sm={8}>

+                                 <input type="checkbox" onChange={this.props.handleChange} defaultChecked={this.state.nsreferralonscopedsearch} className="ds-config-checkbox" id="nsreferralonscopedsearch" />

+                             </Col>

+                         </Row>

+                     </Form>

                      <hr />

                  </CustomCollapse>

                  <div className="ds-margin-top-lg">

                      <button onClick={this.saveLink} className="btn btn-primary">Save Configuration</button>

                  </div>

-                 <ConfirmPopup

+                 <DoubleConfirmModal

                      showModal={this.state.showDeleteConfirm}

                      closeHandler={this.closeDeleteConfirm}

-                     actionFunc={this.deleteLink}

-                     actionParam={this.props.suffix}

-                     msg="Are you really sure you want to delete this database link?"

-                     msgContent={this.props.suffix}

+                     handleChange={this.handleChange}

+                     actionHandler={this.deleteLink}

+                     spinning={this.state.modalSpinning}

+                     item={this.props.suffix}

+                     checked={this.state.modalChecked}

+                     mTitle="Delete Database Link"

+                     mMsg="Are you really sure you want to delete this database link?"

+                     mSpinningMsg="Deleting Database Linkt ..."

+                     mBtnName="Delete Database Link"

                  />

              </div>

          );
@@ -1282,6 +1432,7 @@ 

      addNotification: PropTypes.func,

      reload: PropTypes.func,

      data: PropTypes.object,

+     enableTree: PropTypes.func,

  };

  

  ChainingDatabaseConfig.defaultProps = {
@@ -1289,6 +1440,7 @@ 

      addNotification: noop,

      reload: noop,

      data: {},

+     enableTree: PropTypes.noop,

  };

  

  ChainingConfig.propTypes = {
@@ -1299,6 +1451,7 @@ 

      addNotification: PropTypes.func,

      data: PropTypes.object,

      reload: PropTypes.func,

+     enableTree: PropTypes.func,

  };

  

  ChainingConfig.defaultProps = {
@@ -1309,4 +1462,5 @@ 

      addNotification: noop,

      data: {},

      reload: noop,

+     enableTree: noop,

  };

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

      Row,

      Col,

      ControlLabel,

+     Form,

      Spinner,

      noop

  } from "patternfly-react";
@@ -58,6 +59,10 @@ 

          this.save_db_config = this.save_db_config.bind(this);

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      select_auto_cache (e) {

          this.setState({

              db_cache_auto: !this.state.db_cache_auto
@@ -213,8 +218,8 @@ 

              db_cache_form = <div id="auto-cache-form" className="ds-margin-left">

                  <div>

                      <label htmlFor="autosize" className="ds-config-label-xlrg"

-                         title="Enable database and entry cache auto-tuning using a percentage of the systems current resources (nsslapd-cache-autosize).">

-                         System Memory Percentage</label><input className="ds-input" type="text"

+                         title="Enable database and entry cache auto-tuning using a percentage of the system's current resources (nsslapd-cache-autosize).">

+                         Memory Percentage</label><input className="ds-input" type="text"

                          id="autosizee" size="10" onChange={this.handleChange}

                          value={this.state.autosize} />

                  </div>
@@ -231,7 +236,7 @@ 

              db_cache_form = <div id="manual-cache-form" className="ds-margin-left">

                  <label htmlFor="dbcachesize" className="ds-config-label-xlrg"

                      title="Specifies the database index cache size in bytes (nsslapd-dbcachesize).">

-                     Database Cache Size (bytes)</label><input className="ds-input" type="text"

+                     Database Cache Size</label><input className="ds-input" type="text"

                      id="dbcachesize" size="10" onChange={this.handleChange} value={this.state.dbcachesize} />

              </div>;

              db_auto_checked = false;
@@ -249,8 +254,8 @@ 

          } else {

              import_cache_form = <div id="manual-import-cache-form" className="ds-margin-left">

                  <label htmlFor="importcachesize" className="ds-config-label-xlrg"

-                     title="The size of the database cache used in the bulk import process. (nsslapd-import-cachesize).">

-                     Import Cache Size (bytes)</label><input className="ds-input" type="text" id="importcachesize"

+                     title="The size of the database cache in bytes used in the bulk import process. (nsslapd-import-cachesize).">

+                     Import Cache Size</label><input className="ds-input" type="text" id="importcachesize"

                      size="10" onChange={this.handleChange} value={this.state.importcachesize} />

              </div>;

              import_auto_checked = false;
@@ -259,10 +264,9 @@ 

          let spinner = "";

          if (this.state.loading) {

              spinner =

-                 <div className="ds-loading-spinner ds-center">

-                     <p />

+                 <div className="ds-loading-spinner ds-margin-top ds-center">

                      <h4>Loading global database configuration ...</h4>

-                     <Spinner loading size="md" />

+                     <Spinner className="ds-margin-top" loading size="md" />

                  </div>;

          }

  
@@ -272,15 +276,15 @@ 

                  <div className={this.state.loading ? 'ds-fadeout' : 'ds-fadein'}>

                      <h3 className="ds-config-header">Global Database Configuration</h3>

                      <hr />

-                     <div>

+                     <Form horizontal>

                          <Row

                              title="The maximum number of entries that the Directory Server will check when examining candidate entries in response to a search request (nsslapd-lookthrough-limit)."

                              className="ds-margin-top"

                          >

-                             <Col componentClass={ControlLabel} sm={4}>

+                             <Col componentClass={ControlLabel} sm={5}>

                                  Database Look Though Limit

                              </Col>

-                             <Col sm={8}>

+                             <Col sm={4}>

                                  <input

                                      id="looklimit"

                                      value={this.state.looklimit}
@@ -292,10 +296,10 @@ 

                              title="The number of entry IDs that are searched during a search operation (nsslapd-idlistscanlimit)."

                              className="ds-margin-top"

                          >

-                             <Col componentClass={ControlLabel} sm={4}>

+                             <Col componentClass={ControlLabel} sm={5}>

                                  ID List Scan Limit

                              </Col>

-                             <Col sm={8}>

+                             <Col sm={4}>

                                  <input

                                      id="idscanlimit"

                                      value={this.state.idscanlimit}
@@ -307,10 +311,10 @@ 

                              title="The maximum number of entries that the Directory Server will check when examining candidate entries for a search which uses the simple paged results control (nsslapd-pagedlookthroughlimit)."

                              className="ds-margin-top"

                          >

-                             <Col componentClass={ControlLabel} sm={4}>

+                             <Col componentClass={ControlLabel} sm={5}>

                                  Paged Search Look Through Limit

                              </Col>

-                             <Col sm={8}>

+                             <Col sm={4}>

                                  <input

                                      id="pagelooklimit"

                                      value={this.state.pagelooklimit}
@@ -322,10 +326,10 @@ 

                              title="The number of entry IDs that are searched, specifically, for a search operation using the simple paged results control (nsslapd-pagedidlistscanlimit)."

                              className="ds-margin-top"

                          >

-                             <Col componentClass={ControlLabel} sm={4}>

+                             <Col componentClass={ControlLabel} sm={5}>

                                  Paged Search ID List Scan Limit

                              </Col>

-                             <Col sm={8}>

+                             <Col sm={4}>

                                  <input

                                      id="pagescanlimit"

                                      value={this.state.pagescanlimit}
@@ -337,10 +341,10 @@ 

                              title="The maximum number of entries that the Directory Server will check when examining candidate entries in response to a range search request (nsslapd-rangelookthroughlimit)."

                              className="ds-margin-top"

                          >

-                             <Col componentClass={ControlLabel} sm={4}>

+                             <Col componentClass={ControlLabel} sm={5}>

                                  Range Search Look Through Limit

                              </Col>

-                             <Col sm={8}>

+                             <Col sm={4}>

                                  <input

                                      id="rangelooklimit"

                                      value={this.state.rangelooklimit}
@@ -348,7 +352,7 @@ 

                                  />

                              </Col>

                          </Row>

-                     </div>

+                     </Form>

  

                      <div className="ds-container">

                          <div>
@@ -377,7 +381,7 @@ 

                      <CustomCollapse>

                          <div className="ds-margin-top">

                              <div className="ds-margin-left">

-                                 <div>

+                                 <Form horizontal>

                                      <Row className="ds-margin-top" title="Database Transaction Log Location (nsslapd-db-logdirectory).">

                                          <Col componentClass={ControlLabel} sm={4}>

                                              Transaction Logs Directory
@@ -418,12 +422,10 @@ 

                                              <input id="compactinterval" value={this.state.compactinterval} onChange={this.handleChange} className="ds-input-auto" type="text" />

                                          </Col>

                                      </Row>

-                                 </div>

+                                 </Form>

                              </div>

-                             <p />

                          </div>

                      </CustomCollapse>

-                     <p />

                      <div className="ds-margin-top-lg">

                          <button className="btn btn-primary save-button"

                              onClick={this.save_db_config}>Save Configuration</button>
@@ -441,6 +443,7 @@ 

      addNotification: PropTypes.func,

      data: PropTypes.object,

      reload: PropTypes.func,

+     enableTree: PropTypes.func,

  };

  

  GlobalDatabaseConfig.defaultProps = {
@@ -448,4 +451,5 @@ 

      addNotification: noop,

      data: {},

      reload: noop,

+     enableTree: noop,

  };

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

  import {

      Modal,

      Row,

+     Checkbox,

      Col,

      ControlLabel,

      Radio,
@@ -45,7 +46,7 @@ 

                          </Modal.Title>

                      </Modal.Header>

                      <Modal.Body>

-                         <Form horizontal autoComplete="off">

+                         <Form horizontal>

                              <div>

                                  <label htmlFor="createLinkSuffix" className="ds-config-label" title="The RDN of the link suffix">

                                      Link Sub-Suffix</label><input className={error.createLinkSuffix ? "ds-input-bad ds-input-right" : "ds-input ds-input-right"} onChange={handleChange} type="text" id="createLinkSuffix" size="12" /><b><font color="blue"> ,{suffix}</font></b>
@@ -154,8 +155,7 @@ 

                                      <ControlLabel><b><font color="blue">,{suffix}</font></b></ControlLabel>

                                  </Col>

                              </Row>

-                             <p />

-                             <Row title="The name for the backend database, like 'userroot'.  The name can be a combination of alphanumeric characters, dashes (-), and underscores (_). No other characters are allowed, and the name must be unique across all backends.">

+                             <Row className="ds-margin-top" title="The name for the backend database, like 'userroot'.  The name can be a combination of alphanumeric characters, dashes (-), and underscores (_). No other characters are allowed, and the name must be unique across all backends.">

                                  <Col sm={3}>

                                      <ControlLabel>Database Name</ControlLabel>

                                  </Col>
@@ -223,7 +223,7 @@ 

          if (spinning) {

              spinner =

                  <Row>

-                     <div className="ds-modal-spinner">

+                     <div className="ds-margin-top-lg ds-modal-spinner">

                          <Spinner loading inline size="lg" />Exporting database... <font size="1">(You can safely close this window)</font>

                      </div>

                  </Row>;
@@ -260,7 +260,17 @@ 

                                      />

                                  </Col>

                              </Row>

-                             <p />

+                             <Row className="ds-margin-top-xlg">

+                                 <Col sm={12} className="ds-margin-left">

+                                     <Checkbox

+                                         id="includeReplData"

+                                         onChange={handleChange}

+                                         title="Include the replication metadata needed to restore or initialize another replica."

+                                     >

+                                         Include Replication Data

+                                     </Checkbox>

+                                 </Col>

+                             </Row>

                              {spinner}

                          </Form>

                      </Modal.Body>
@@ -302,8 +312,7 @@ 

          if (spinning) {

              spinner =

                  <Row>

-                     <div className="ds-modal-spinner">

-                         <p />

+                     <div className="ds-margin-top-lg ds-modal-spinner">

                          <Spinner loading inline size="lg" />Importing LDIF file... <font size="1">(You can safely close this window)</font>

                      </div>

                  </Row>;
@@ -331,7 +340,6 @@ 

                          </Modal.Title>

                      </Modal.Header>

                      <Modal.Body>

-                         <p />

                          <LDIFTable

                              rows={suffixRows}

                              confirmImport={this.props.showConfirmImport}
@@ -358,7 +366,6 @@ 

                                      </Button>

                                  </Col>

                              </Row>

-                             <p />

                              {spinner}

                          </Form>

                      </Modal.Body>

@@ -665,7 +665,6 @@ 

                                      </Col>

                                  </Row>

                              </div>

-                             <p />

                              <Row className="ds-margin-top-lg">

                                  <Col sm={12} title="List of matching rules separated by a 'space'">

                                      <p><b>Matching Rules</b></p>
@@ -836,7 +835,6 @@ 

                                      </Col>

                                  </Row>

                              </div>

-                             <p />

                              <Row className="ds-margin-top-lg">

                                  <Col sm={12}>

                                      <p><b>Matching Rules</b></p>

@@ -295,8 +295,7 @@ 

                                      </select>

                                  </Col>

                              </Row>

-                             <p />

-                             <Row>

+                             <Row className="ds-margin-top">

                                  <Col sm={3}>

                                      <ControlLabel>Host Name</ControlLabel>

                                  </Col>
@@ -304,8 +303,7 @@ 

                                      <input className={error.refHost ? "ds-input-auto-bad" : "ds-input-auto"} type="text" onChange={handleChange} id="refHost" />

                                  </Col>

                              </Row>

-                             <p />

-                             <Row>

+                             <Row className="ds-margin-top">

                                  <Col sm={3}>

                                      <ControlLabel>Port Number</ControlLabel>

                                  </Col>
@@ -313,8 +311,7 @@ 

                                      <input className={error.refPort ? "ds-input-auto-bad" : "ds-input-auto"} type="text" onChange={handleChange} id="refPort" />

                                  </Col>

                              </Row>

-                             <p />

-                             <Row>

+                             <Row className="ds-margin-top">

                                  <Col sm={3}>

                                      <ControlLabel>Suffix</ControlLabel>

                                  </Col>
@@ -322,8 +319,7 @@ 

                                      <input className="ds-input-auto" onChange={handleChange} type="text" id="refSuffix" />

                                  </Col>

                              </Row>

-                             <p />

-                             <Row title="Comma separated list of attributes to return">

+                             <Row className="ds-margin-top" title="Comma separated list of attributes to return">

                                  <Col sm={3}>

                                      <ControlLabel>Attributes</ControlLabel>

                                  </Col>
@@ -331,8 +327,7 @@ 

                                      <input className="ds-input-auto" onChange={handleChange} type="text" id="refAttrs" />

                                  </Col>

                              </Row>

-                             <p />

-                             <Row>

+                             <Row className="ds-margin-top">

                                  <Col sm={3}>

                                      <ControlLabel>Filter</ControlLabel>

                                  </Col>
@@ -340,13 +335,12 @@ 

                                      <input onChange={handleChange} className="ds-input-auto" type="text" id="refFilter" />

                                  </Col>

                              </Row>

-                             <p />

-                             <Row>

+                             <Row className="ds-margin-top">

                                  <Col sm={3}>

                                      <ControlLabel>Scope</ControlLabel>

                                  </Col>

                                  <Col sm={9}>

-                                     <select className="btn btn-default dropdown" onChange={handleChange} defaultValue="" name="refScope">

+                                     <select className="btn btn-default dropdown" onChange={handleChange} defaultValue="" id="refScope">

                                          <option />

                                          <option>sub</option>

                                          <option>one</option>

@@ -1,6 +1,6 @@ 

  import cockpit from "cockpit";

  import React from "react";

- import { ConfirmPopup } from "../notifications.jsx";

+ import { DoubleConfirmModal } from "../notifications.jsx";

  import { AttrEncryption } from "./attrEncryption.jsx";

  import { SuffixConfig } from "./suffixConfig.jsx";

  import { SuffixReferrals } from "./referrals.jsx";
@@ -73,6 +73,9 @@ 

              importSpinner: false,

              showConfirmLDIFImport: false,

              deleleLDIFName: "",

+             modalChecked: false,

+             modalSpinning: false,

+             includeReplData: false,

              // Reindex all

              showReindexConfirm: false,

              // Create Sub Suffix
@@ -128,12 +131,15 @@ 

          this.handleLinkChange = this.handleLinkChange.bind(this);

          // Suffix config

          this.saveSuffixConfig = this.saveSuffixConfig.bind(this);

-         // Attr Encrypt Modal

          this.showDeleteConfirm = this.showDeleteConfirm.bind(this);

          this.closeDeleteConfirm = this.closeDeleteConfirm.bind(this);

          this.doDelete = this.doDelete.bind(this);

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      handleNavSelect(key) {

          this.setState({ activeKey: key });

      }
@@ -176,6 +182,8 @@ 

          this.setState({

              showConfirmLDIFImport: true,

              importLDIFName: item.name,

+             modalChecked: false,

+             modalSpinning: false,

          });

      }

  
@@ -183,6 +191,8 @@ 

          // call importLDIF

          this.setState({

              showConfirmLDIFImport: false,

+             modalChecked: false,

+             modalSpinning: false,

          });

      }

  
@@ -240,6 +250,7 @@ 

              attrEncryption: false,

              showExportModal: true,

              exportSpinner: false,

+             includeReplData: false,

              errObj: {},

          });

      }
@@ -288,6 +299,10 @@ 

              export_cmd.push("--encrypted");

          }

  

+         if (this.state.includeReplData) {

+             export_cmd.push("--replication");

+         }

+ 

          this.setState({

              exportSpinner: true,

          });
@@ -323,13 +338,17 @@ 

      //

      showReindexConfirm() {

          this.setState({

-             showReindexConfirm: true

+             showReindexConfirm: true,

+             modalChecked: false,

+             modalSpinning: false,

          });

      }

  

      closeReindexConfirm() {

          this.setState({

-             showReindexConfirm: false

+             showReindexConfirm: false,

+             modalChecked: false,

+             modalSpinning: false,

          });

      }

  
@@ -624,13 +643,17 @@ 

      //

      showDeleteConfirm(item) {

          this.setState({

-             showDeleteConfirm: true

+             showDeleteConfirm: true,

+             modalSpinning: false,

+             modalChecked: false

          });

      }

  

      closeDeleteConfirm() {

          this.setState({

-             showDeleteConfirm: false

+             showDeleteConfirm: false,

+             modalChecked: false,

+             modalSpinning: false,

          });

      }

  
@@ -733,11 +756,6 @@ 

              suffixIcon = "leaf";

          }

  

-         const confirm_msg =

-             <span>

-                 Are you sure you want to import: <b>{this.state.importLDIFName}</b> ?

-             </span>;

- 

          return (

              <div id="suffix-page">

                  <Row>
@@ -746,7 +764,7 @@ 

                      </Col>

                      <Col sm={2}>

                          <div>

-                             <DropdownButton className="ds-action-button" bsStyle="primary" title="Suffix Tasks" id="mydropdown">

+                             <DropdownButton className="ds-action-button" bsStyle="primary" title="Suffix Tasks" id="mydropdown" pullRight>

                                  <MenuItem eventKey="1" onClick={this.showImportModal} title="Import an LDIF file to initialize the database">

                                      Initialize Suffix

                                  </MenuItem>
@@ -770,10 +788,9 @@ 

                          </div>

                      </Col>

                  </Row>

-                 <p />

  

                  <TabContainer id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>

-                     <div>

+                     <div className="ds-margin-top-xlg">

                          <Nav bsClass="nav nav-tabs nav-tabs-pf">

                              <NavItem eventKey={1}>

                                  <div dangerouslySetInnerHTML={{__html: 'Settings'}} />
@@ -856,14 +873,18 @@ 

                          </TabContent>

                      </div>

                  </TabContainer>

- 

-                 <ConfirmPopup

+                 <DoubleConfirmModal

                      showModal={this.state.showDeleteConfirm}

                      closeHandler={this.closeDeleteConfirm}

-                     actionFunc={this.doDelete}

-                     actionParam={this.props.suffix}

-                     msg="Are you really sure you want to delete the database?"

-                     msgContent={this.props.suffix}

+                     handleChange={this.handleChange}

+                     actionHandler={this.doDelete}

+                     spinning={this.state.modalSpinning}

+                     item={this.props.suffix}

+                     checked={this.state.modalChecked}

+                     mTitle="Delete Replication Agreement"

+                     mMsg="Are you really sure you want to delete the database?"

+                     mSpinningMsg="Deleting database ..."

+                     mBtnName="Delete Database"

                  />

                  <CreateLinkModal

                      showModal={this.state.showLinkModal}
@@ -896,15 +917,19 @@ 

                      rows={this.props.LDIFRows}

                      suffix={this.props.suffix}

                  />

-                 <ConfirmPopup

+                 <DoubleConfirmModal

                      showModal={this.state.showConfirmLDIFImport}

                      closeHandler={this.closeConfirmLDIFImport}

-                     actionFunc={this.importLDIF}

-                     actionParam={this.state.importLDIFName}

-                     msg={confirm_msg}

-                     msgContent="WARNING: This action will permanently overwrite the current database!"

+                     handleChange={this.handleChange}

+                     actionHandler={this.importLDIF}

+                     spinning={this.state.modalSpinning}

+                     item={this.state.importLDIFName}

+                     checked={this.state.modalChecked}

+                     mTitle="Initialize Database From LDIF"

+                     mMsg="Are you sure you want to initialize the database (it will permanently overwrite the current database)?"

+                     mSpinningMsg="Initializing Database ..."

+                     mBtnName="Initialize Database"

                  />

- 

                  <ExportModal

                      showModal={this.state.showExportModal}

                      closeHandler={this.closeExportModal}
@@ -913,13 +938,18 @@ 

                      spinning={this.state.exportSpinner}

                      error={this.state.errObj}

                  />

-                 <ConfirmPopup

+                 <DoubleConfirmModal

                      showModal={this.state.showReindexConfirm}

                      closeHandler={this.closeReindexConfirm}

-                     actionFunc={this.doReindex}

-                     actionParam={this.props.suffix}

-                     msg="Are you sure you want to reindex all the attribute indexes?"

-                     msgContent=""

+                     handleChange={this.handlChange}

+                     actionHandler={this.doReindex}

+                     spinning={this.state.modalSpinning}

+                     item={this.props.suffix}

+                     checked={this.state.modalChecked}

+                     mTitle="Reindex All Attributes"

+                     mMsg="Are you sure you want to reindex all the attribute indexes?"

+                     mSpinningMsg="Reindexing Database ..."

+                     mBtnName="Reindex"

                  />

                  <ReindexModal

                      showModal={this.state.showReindexModal}
@@ -949,6 +979,7 @@ 

      data: PropTypes.object,

      attrs: PropTypes.array,

      LDIFRows: PropTypes.array,

+     enableTree: PropTypes.func,

  };

  

  Suffix.defaultProps = {
@@ -966,5 +997,6 @@ 

      dbtype: "",

      data: {},

      attrs: [],

-     LDIFRows: []

+     LDIFRows: [],

+     enableTree: PropTypes.noop,

  };

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

      Row,

      Col,

      ControlLabel,

+     Form,

  } from "patternfly-react";

  

  export class SuffixConfig extends React.Component {
@@ -15,76 +16,82 @@ 

              const cacheValue = this.props.cachesize + "  (auto-sized)";

              const cachememValue = this.props.cachememsize + "  (auto-sized)";

              cacheInputs =

-                 <div>

-                     <Row className="ds-margin-top" title="The entry cache size setting is being auto-sized and is read-only - see Global Database Configuration">

-                         <Col componentClass={ControlLabel} sm={3}>

-                             Entry Cache Size (bytes)

+                 <Form horizontal>

+                     <Row className="ds-margin-top" title="The entry cache size in bytes setting is being auto-sized and is read-only - see Global Database Configuration">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             Entry Cache Size

                          </Col>

-                         <Col sm={7}>

+                         <Col sm={8}>

                              <input disabled value={cachememValue} className="ds-input-auto" type="text" id="cachememsize" />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top" title="The entry cache max entries setting is being auto-sized and is read-only - see Global Database Configuration">

-                         <Col componentClass={ControlLabel} sm={3}>

+                         <Col componentClass={ControlLabel} sm={4}>

                              Entry Cache Max Entries

                          </Col>

-                         <Col sm={7}>

+                         <Col sm={8}>

                              <input disabled value={cacheValue} className="ds-input-auto" type="text" id="cachesize" />

                          </Col>

                      </Row>

-                     <Row className="ds-margin-top" title="The available memory space for the DN cache. The DN cache is similar to the entry cache for a database, only its table stores only the entry ID and the entry DN (nsslapd-dncachememsize).">

-                         <Col componentClass={ControlLabel} sm={3}>

-                             DN Cache Size (bytes)

+                     <Row className="ds-margin-top" title="The available memory space in bytes for the DN cache. The DN cache is similar to the entry cache for a database, only its table stores only the entry ID and the entry DN (nsslapd-dncachememsize).">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             DN Cache Size

                          </Col>

-                         <Col sm={7}>

+                         <Col sm={8}>

                              <input onChange={this.props.handleChange} value={this.props.dncachememsize} className="ds-input-auto" type="text" id="dncachememsize" />

                          </Col>

                      </Row>

-                 </div>;

+                 </Form>;

          } else {

              cacheInputs =

-                 <div>

-                     <Row className="ds-margin-top" title="The size for the available memory space for the entry cache (nsslapd-cachememsize).">

-                         <Col componentClass={ControlLabel} sm={3}>

-                             Entry Cache Size (bytes)

+                 <Form horizontal>

+                     <Row className="ds-margin-top" title="The size for the available memory space in bytes for the entry cache (nsslapd-cachememsize).">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             Entry Cache Size

                          </Col>

-                         <Col sm={7}>

+                         <Col sm={8}>

                              <input onChange={this.props.handleChange} value={this.props.cachememsize} className="ds-input-auto" type="text" id="cachememsize" />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top" title="The number of entries to keep in the entry cache, use'-1' for unlimited (nsslapd-cachesize).">

-                         <Col componentClass={ControlLabel} sm={3}>

+                         <Col componentClass={ControlLabel} sm={4}>

                              Entry Cache Max Entries

                          </Col>

-                         <Col sm={7}>

+                         <Col sm={8}>

                              <input onChange={this.props.handleChange} value={this.props.cachesize} className="ds-input-auto" type="text" id="cachesize" />

                          </Col>

                      </Row>

-                     <Row className="ds-margin-top" title="the available memory space for the DN cache. The DN cache is similar to the entry cache for a database, only its table stores only the entry ID and the entry DN (nsslapd-dncachememsize).">

-                         <Col componentClass={ControlLabel} sm={3}>

-                             DN Cache Size (bytes)

+                     <Row className="ds-margin-top" title="the available memory space in bytes for the DN cache. The DN cache is similar to the entry cache for a database, only its table stores only the entry ID and the entry DN (nsslapd-dncachememsize).">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             DN Cache Size

                          </Col>

-                         <Col sm={7}>

+                         <Col sm={8}>

                              <input onChange={this.props.handleChange} value={this.props.dncachememsize} className="ds-input-auto" type="text" id="dncachememsize" />

                          </Col>

                      </Row>

-                 </div>;

+                 </Form>;

          }

          return (

              <div className="ds-margin-top-lg">

-                 <p />

                  {cacheInputs}

-                 <p />

-                 <div>

-                     <div>

-                         <input type="checkbox" onChange={this.props.handleChange} checked={this.props.readOnly} className="ds-config-checkbox" id="readOnly" /><label

-                             htmlFor="readOnly" className="ds-label" title="Put database in Read-Only mode (nsslapd-readonly)."> Database Read-Only Mode</label>

-                     </div>

-                     <div>

-                         <input type="checkbox" onChange={this.props.handleChange} checked={this.props.requireIndex} className="ds-config-checkbox" id="requireIndex" /><label

-                             htmlFor="requireIndex" className="ds-label" title="Block unindexed searches on this suffix (nsslapd-require-index)."> Block Unindexed Searches</label>

-                     </div>

-                 </div>

+                 <Form horizontal>

+                     <Row className="ds-margin-top-lg" title="Put database in Read-Only mode (nsslapd-readonly).">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             Database Read-Only Mode

+                         </Col>

+                         <Col sm={8}>

+                             <input type="checkbox" onChange={this.props.handleChange} checked={this.props.readOnly} className="ds-config-checkbox" id="readOnly" />

+                         </Col>

+                     </Row>

+                     <Row className="ds-margin-top" title="Block unindexed searches on this suffix (nsslapd-require-index).">

+                         <Col componentClass={ControlLabel} sm={4}>

+                             Block Unindexed Searches

+                         </Col>

+                         <Col sm={8}>

+                             <input type="checkbox" onChange={this.props.handleChange} checked={this.props.requireIndex} className="ds-config-checkbox" id="requireIndex" />

+                         </Col>

+                     </Row>

+                 </Form>

                  <div className="ds-margin-top-lg">

                      <button className="btn btn-primary save-button" onClick={this.props.saveHandler}>Save Configuration</button>

                  </div>

@@ -831,8 +831,7 @@ 

                                      {nameInput}

                                  </Col>

                              </Row>

-                             <p />

-                             <Row>

+                             <Row className="ds-margin-top">

                                  <Col sm={3}>

                                      <ControlLabel>Search Base</ControlLabel>

                                  </Col>
@@ -841,8 +840,7 @@ 

                                          onChange={handleChange} type="text" id="vlvBase" defaultValue={base} />

                                  </Col>

                              </Row>

-                             <p />

-                             <Row>

+                             <Row className="ds-margin-top">

                                  <Col sm={3}>

                                      <ControlLabel>Search Filter</ControlLabel>

                                  </Col>
@@ -851,16 +849,14 @@ 

                                          onChange={handleChange} type="text" id="vlvFilter" defaultValue={filter} />

                                  </Col>

                              </Row>

-                             <p />

                              {vlvscope}

                              <hr />

                              <div>

-                                 <p />

-                                 <div>

+                                 <div className="ds-margin-top">

                                      {sortTable}

-                                     <p />

                                      <Typeahead

                                          multiple

+                                         className="ds-margin-top"

                                          id="vlvsortindex"

                                          onChange={values => {

                                              this.handleTypeaheadChange(values);
@@ -870,8 +866,7 @@ 

                                          placeholder="Start typing attribute names to create a sort index"

                                          ref={(typeahead) => { this.typeahead = typeahead }}

                                      />

-                                     <p />

-                                     <button type="button" onClick={this.updateSorts}>Add Sort Index</button>

+                                     <button className="ds-margin-top" type="button" onClick={this.updateSorts}>Add Sort Index</button>

                                  </div>

                              </div>

                              <hr />

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

          textarea.scrollTop = textarea.scrollHeight;

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      render() {

          let spinner = "";

          if (this.props.reloading) {
@@ -73,8 +77,7 @@ 

                          {spinner}

                      </Col>

                  </Row>

-                 <p />

-                 <Row>

+                 <Row className="ds-margin-top-lg">

                      <Col sm={6}>

                          {selectLines}

                      </Col>
@@ -102,6 +105,7 @@ 

      reloading: PropTypes.bool,

      refreshing: PropTypes.bool,

      lines: PropTypes.string,

+     enableTree: PropTypes.func,

  };

  

  AccessLogMonitor.defaultProps = {
@@ -111,7 +115,8 @@ 

      reload: noop,

      reloading: false,

      refreshing: false,

-     line: "50"

+     line: "50",

+     enableTree: noop,

  };

  

  export default AccessLogMonitor;

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

          textarea.scrollTop = textarea.scrollHeight;

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      render() {

          let spinner = "";

          if (this.props.reloading) {
@@ -60,7 +64,7 @@ 

          return (

              <div id="monitor-log-auditfail-page">

                  <Row>

-                     <Col sm={3}>

+                     <Col sm={4}>

                          <ControlLabel className="ds-suffix-header">

                              Audit Failure Log

                              <Icon className="ds-left-margin ds-refresh"
@@ -69,12 +73,11 @@ 

                              />

                          </ControlLabel>

                      </Col>

-                     <Col sm={9} className="ds-float-left">

+                     <Col sm={8} className="ds-float-left">

                          {spinner}

                      </Col>

                  </Row>

-                 <p />

-                 <Row>

+                 <Row className="ds-margin-top-lg">

                      <Col sm={6}>

                          {selectLines}

                      </Col>
@@ -102,6 +105,7 @@ 

      reloading: PropTypes.bool,

      refreshing: PropTypes.bool,

      lines: PropTypes.string,

+     enableTree: PropTypes.func,

  };

  

  AuditFailLogMonitor.defaultProps = {
@@ -111,7 +115,8 @@ 

      reload: noop,

      reloading: false,

      refreshing: false,

-     line: "50"

+     line: "50",

+     enableTree: noop,

  };

  

  export default AuditFailLogMonitor;

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

          textarea.scrollTop = textarea.scrollHeight;

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      render() {

          let spinner = "";

          if (this.props.reloading) {
@@ -60,7 +64,7 @@ 

          return (

              <div id="monitor-log-audit-page">

                  <Row>

-                     <Col sm={3}>

+                     <Col sm={4}>

                          <ControlLabel className="ds-suffix-header">

                              Audit Log

                              <Icon className="ds-left-margin ds-refresh"
@@ -69,12 +73,11 @@ 

                              />

                          </ControlLabel>

                      </Col>

-                     <Col sm={9} className="ds-float-left">

+                     <Col sm={8} className="ds-float-left">

                          {spinner}

                      </Col>

                  </Row>

-                 <p />

-                 <Row>

+                 <Row className="ds-margin-top-lg">

                      <Col sm={6}>

                          {selectLines}

                      </Col>
@@ -102,6 +105,7 @@ 

      reloading: PropTypes.bool,

      refreshing: PropTypes.bool,

      lines: PropTypes.string,

+     enableTree: PropTypes.func,

  };

  

  AuditLogMonitor.defaultProps = {
@@ -111,7 +115,8 @@ 

      reload: noop,

      reloading: false,

      refreshing: false,

-     line: "50"

+     line: "50",

+     enableTree: noop,

  };

  

  export default AuditLogMonitor;

@@ -6,9 +6,14 @@ 

      Col,

      ControlLabel,

      Icon,

+     noop

  } from "patternfly-react";

  

  export class ChainingMonitor extends React.Component {

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      render() {

          return (

              <div id="monitor-server-page" className="container-fluid">
@@ -23,9 +28,7 @@ 

                          </ControlLabel>

                      </Col>

                  </Row>

-                 <p />

-                 <hr />

-                 <div>

+                 <div className="ds-margin-top-lg">

                      <Row className="ds-margin-top">

                          <Col sm={3}>

                              <ControlLabel>
@@ -165,13 +168,15 @@ 

  ChainingMonitor.propTypes = {

      suffix: PropTypes.string,

      bename: PropTypes.string,

-     data: PropTypes.object

+     data: PropTypes.object,

+     enableTree: PropTypes.func,

  };

  

  ChainingMonitor.defaultProps = {

      suffix: "",

      bename: "",

-     data: {}

+     data: {},

+     enableTree: noop,

  };

  

  export default ChainingMonitor;

@@ -12,6 +12,7 @@ 

      Row,

      Col,

      ControlLabel,

+     Form,

      Icon,

      noop,

  } from "patternfly-react";
@@ -22,10 +23,15 @@ 

          super(props);

          this.state = {

              activeKey: 1,

+             disableTabs: false,

          };

          this.handleNavSelect = this.handleNavSelect.bind(this);

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      handleNavSelect(key) {

          this.setState({ activeKey: key });

      }
@@ -82,7 +88,7 @@ 

          }

  

          return (

-             <div id="db-content" className="container-fluid">

+             <div id="db-content">

                  <Row>

                      <Col sm={12} className="ds-word-wrap">

                          <ControlLabel className="ds-suffix-header">
@@ -94,7 +100,7 @@ 

                          </ControlLabel>

                      </Col>

                  </Row>

-                 <TabContainer id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>

+                 <TabContainer className="ds-margin-top-lg" id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>

                      <div>

                          <Nav bsClass="nav nav-tabs nav-tabs-pf">

                              <NavItem eventKey={1}>
@@ -125,83 +131,70 @@ 

                                      <b className="ds-left-margin">DB Cache Hit Ratio</b>

                                  </div>

                                  <hr />

-                                 <div>

+                                 <Form horizontal>

                                      <Row className="ds-margin-top">

-                                         <Col sm={4}>

-                                             <ControlLabel>

-                                                 Database Cache Hit Ratio

-                                             </ControlLabel>

+                                         <Col componentClass={ControlLabel} sm={3}>

+                                             Database Cache Hit Ratio

                                          </Col>

                                          <Col sm={3}>

                                              <input type="text" value={this.props.data.dbcachehitratio} size="28" readOnly />

                                          </Col>

                                      </Row>

                                      <Row className="ds-margin-top">

-                                         <Col sm={4}>

-                                             <ControlLabel>

-                                                 Database Cache Tries

-                                             </ControlLabel>

+                                         <Col componentClass={ControlLabel} sm={3}>

+                                             Database Cache Tries

                                          </Col>

                                          <Col sm={3}>

                                              <input type="text" value={this.props.data.dbcachetries} size="28" readOnly />

                                          </Col>

                                      </Row>

                                      <Row className="ds-margin-top">

-                                         <Col sm={4}>

-                                             <ControlLabel>

-                                                 Database Cache Hits

-                                             </ControlLabel>

+                                         <Col componentClass={ControlLabel} sm={3}>

+                                             Database Cache Hits

                                          </Col>

                                          <Col sm={3}>

                                              <input type="text" value={this.props.data.dbcachehits} size="28" readOnly />

                                          </Col>

                                      </Row>

                                      <Row className="ds-margin-top">

-                                         <Col sm={4}>

-                                             <ControlLabel>

-                                                 Cache Pages Read

-                                             </ControlLabel>

+                                         <Col componentClass={ControlLabel} sm={3}>

+                                             Cache Pages Read

                                          </Col>

                                          <Col sm={3}>

                                              <input type="text" value={this.props.data.dbcachepagein} size="28" readOnly />

                                          </Col>

                                      </Row>

                                      <Row className="ds-margin-top">

-                                         <Col sm={4}>

-                                             <ControlLabel>

-                                                 Cache Pages Written

-                                             </ControlLabel>

+                                         <Col componentClass={ControlLabel} sm={3}>

+                                             Cache Pages Written

                                          </Col>

                                          <Col sm={3}>

                                              <input type="text" value={this.props.data.dbcachepageout} size="28" readOnly />

                                          </Col>

                                      </Row>

                                      <Row className="ds-margin-top">

-                                         <Col sm={4}>

-                                             <ControlLabel>

-                                                 Read-Only Page Evictions

-                                             </ControlLabel>

+                                         <Col componentClass={ControlLabel} sm={3}>

+                                             Read-Only Page Evictions

                                          </Col>

                                          <Col sm={3}>

                                              <input type="text" value={this.props.data.dbcacheroevict} size="28" readOnly />

                                          </Col>

                                      </Row>

                                      <Row className="ds-margin-top">

-                                         <Col sm={4}>

-                                             <ControlLabel>

-                                                 Read-Write Page Evictions

-                                             </ControlLabel>

+                                         <Col componentClass={ControlLabel} sm={3}>

+                                             Read-Write Page Evictions

                                          </Col>

                                          <Col sm={3}>

                                              <input type="text" value={this.props.data.dbcacherwevict} size="28" readOnly />

                                          </Col>

                                      </Row>

-                                 </div>

+                                 </Form>

                              </TabPane>

  

                              <TabPane eventKey={2}>

                                  <div className="ds-margin-top-lg">

                                      <div className="ds-container">

+                                         <div className="ds-divider" />

                                          <div className="ds-left-margin">

                                              <DonutChart

                                                  id="monitor-db-cache-ndn-hitratio-chart"
@@ -219,6 +212,8 @@ 

                                              />

                                              <b>NDN Cache Hit Ratio</b>

                                          </div>

+                                         <div className="ds-divider" />

+                                         <div className="ds-divider" />

                                          <div className="ds-chart-right">

                                              <PieChart

                                                  id="monitor-db-cache-ndn-util-chart"
@@ -243,6 +238,7 @@ 

                                                  }}

                                                  title={{type: 'pie'}}

                                                  legend={{show: true, position: 'right'}}

+                                                 unloadBeforeLoad

                                              />

                                              <b>NDN Cache Utilization</b>

                                              <div>
@@ -251,98 +247,80 @@ 

                                          </div>

                                      </div>

                                      <hr />

-                                     <div>

+                                     <Form horizontal>

                                          <Row className="ds-margin-top">

-                                             <Col sm={4}>

-                                                 <ControlLabel>

-                                                     NDN Cache Hit Ratio

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 NDN Cache Hit Ratio

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.normalizeddncachehitratio} size="28" readOnly />

                                              </Col>

                                          </Row>

                                          <Row className="ds-margin-top">

-                                             <Col sm={4}>

-                                                 <ControlLabel>

-                                                     NDN Cache Tries

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 NDN Cache Tries

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.normalizeddncachetries} size="28" readOnly />

                                              </Col>

                                          </Row>

                                          <Row className="ds-margin-top">

-                                             <Col sm={4}>

-                                                 <ControlLabel>

-                                                     NDN Cache Hits

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 NDN Cache Hits

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.normalizeddncachehits} size="28" readOnly />

                                              </Col>

                                          </Row>

                                          <Row className="ds-margin-top">

-                                             <Col sm={4}>

-                                                 <ControlLabel>

-                                                     NDN Cache Evictions

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 NDN Cache Evictions

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.normalizeddncacheevictions} size="28" readOnly />

                                              </Col>

                                          </Row>

                                          <Row className="ds-margin-top">

-                                             <Col sm={4}>

-                                                 <ControlLabel>

-                                                     NDN Cache Max Size

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 NDN Cache Max Size

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.maxnormalizeddncachesize} size="28" readOnly />

                                              </Col>

                                          </Row>

                                          <Row className="ds-margin-top">

-                                             <Col sm={4}>

-                                                 <ControlLabel>

-                                                     NDN Current Cache Size

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 NDN Current Cache Size

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.currentnormalizeddncachesize} size="28" readOnly />

                                              </Col>

                                          </Row>

                                          <Row className="ds-margin-top">

-                                             <Col sm={4}>

-                                                 <ControlLabel>

-                                                     NDN Cache DN Count

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 NDN Cache DN Count

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.currentnormalizeddncachecount} size="28" readOnly />

                                              </Col>

                                          </Row>

                                          <Row className="ds-margin-top">

-                                             <Col sm={4}>

-                                                 <ControlLabel>

-                                                     NDN Cache Thread Size

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 NDN Cache Thread Size

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.normalizeddncachethreadsize} size="28" readOnly />

                                              </Col>

                                          </Row>

                                          <Row className="ds-margin-top">

-                                             <Col sm={4}>

-                                                 <ControlLabel>

-                                                     NDN Cache Thread Slots

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 NDN Cache Thread Slots

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.normalizeddncachethreadslots} size="28" readOnly />

                                              </Col>

                                          </Row>

-                                     </div>

+                                     </Form>

                                  </div>

                              </TabPane>

                          </TabContent>
@@ -357,12 +335,14 @@ 

  

  DatabaseMonitor.propTypes = {

      data: PropTypes.object,

-     reload: PropTypes.func

+     reload: PropTypes.func,

+     enableTree: PropTypes.func,

  };

  

  DatabaseMonitor.defaultProps = {

      data: {},

-     reload: noop

+     reload: noop,

+     enableTree: noop,

  };

  

  export default DatabaseMonitor;

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

          textarea.scrollTop = textarea.scrollHeight;

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      render() {

          let spinner = "";

          if (this.props.reloading) {
@@ -73,8 +77,7 @@ 

                          {spinner}

                      </Col>

                  </Row>

-                 <p />

-                 <Row>

+                 <Row className="ds-margin-top-lg">

                      <Col sm={5}>

                          {selectLines}

                      </Col>
@@ -122,8 +125,8 @@ 

      handleSevLevel: PropTypes.func,

      refreshing: PropTypes.bool,

      handleRefresh: PropTypes.func,

-     lines: PropTypes.string

- 

+     lines: PropTypes.string,

+     enableTree: PropTypes.func,

  };

  

  ErrorLogMonitor.defaultProps = {
@@ -134,7 +137,8 @@ 

      handleSevLevel: noop,

      refreshing: false,

      handleRefresh: noop,

-     lines: "50"

+     lines: "50",

+     enableTree: noop,

  };

  

  export default ErrorLogMonitor;

@@ -76,8 +76,7 @@ 

                                      />

                                  </Col>

                              </Row>

-                             <p />

-                             <Row>

+                             <Row className="ds-margin-top">

                                  <Col sm={3}>

                                      <ControlLabel>

                                          Password
@@ -559,8 +558,7 @@ 

                                          <Row>

                                              <textarea className="ds-conflict" value={conflict} readOnly />

                                          </Row>

-                                         <p />

-                                         <Row>

+                                         <Row className="ds-margin-top">

                                              <p>Child Entries: <b>{conflictChildren}</b></p>

                                          </Row>

                                      </Col>
@@ -572,8 +570,7 @@ 

                                          <Row>

                                              <textarea className="ds-conflict" value={valid} readOnly />

                                          </Row>

-                                         <p />

-                                         <Row>

+                                         <Row className="ds-margin-top">

                                              <p>Child Entries: <b>{validChildren}</b></p>

                                          </Row>

                                      </Col>
@@ -598,8 +595,7 @@ 

                                          <input onChange={handleConvertChange} type="text" placeholder="Enter new RDN here" size="30" />

                                      </Col>

                                  </Row>

-                                 <p />

-                                 <Row>

+                                 <Row className="ds-margin-top">

                                      <h4>Or, you can replace, or swap, the <b>Valid Entry</b> (and its child entries) with the <b>Conflict Entry</b></h4>

                                  </Row>

                                  <Row>
@@ -615,8 +611,7 @@ 

                                          </Button>

                                      </Col>

                                  </Row>

-                                 <p />

-                                 <Row>

+                                 <Row className="ds-margin-top">

                                      <h4>Or, you can delete the <b>Conflict Entry</b></h4>

                                  </Row>

                                  <Row>

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

          this.closeConfirmSwapConflict = this.closeConfirmSwapConflict.bind(this);

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      convertConflict (dn) {

          let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",

              "repl-conflict", "convert", dn, "--new-rdn=" + this.state.convertRDN];
@@ -753,7 +757,7 @@ 

  

          return (

              <div id="monitor-suffix-page" className="ds-tab-table">

-                 <TabContainer id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>

+                 <TabContainer className="ds-margin-top-lg" id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>

                      <div>

                          <Nav bsClass="nav nav-tabs nav-tabs-pf">

                              <NavItem eventKey={1}>
@@ -777,8 +781,8 @@ 

                                          pokeAgmt={this.pokeAgmt}

                                          viewAgmt={this.showAgmtModal}

                                      />

-                                     <p />

                                      <Button

+                                         className="ds-margin-top"

                                          bsStyle="primary"

                                          onClick={this.getLagReportCreds}

                                          title="Display report that shows the lag time and replication status of each agreement in relationship to its replica"
@@ -907,6 +911,7 @@ 

      reloadAgmts: PropTypes.func,

      reloadWinsyncAgmts: PropTypes.func,

      reloadConflicts: PropTypes.func,

+     enableTree: PropTypes.func,

  };

  

  ReplMonitor.defaultProps = {
@@ -917,6 +922,7 @@ 

      reloadAgmts: noop,

      reloadWinsyncAgmts: noop,

      reloadConflicts: noop,

+     enableTree: noop,

  };

  

  export default ReplMonitor;

@@ -14,6 +14,7 @@ 

      Row,

      Col,

      ControlLabel,

+     Form,

      Icon,

      noop

  } from "patternfly-react";
@@ -27,6 +28,10 @@ 

          this.handleNavSelect = this.handleNavSelect.bind(this);

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      handleNavSelect(key) {

          this.setState({ activeKey: key });

      }
@@ -52,7 +57,7 @@ 

                      </Col>

                  </Row>

  

-                 <TabContainer id="server-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>

+                 <TabContainer className="ds-margin-top-lg" id="server-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>

                      <div>

                          <Nav bsClass="nav nav-tabs nav-tabs-pf">

                              <NavItem eventKey={1}>
@@ -65,7 +70,7 @@ 

                          <TabContent>

  

                              <TabPane eventKey={1}>

-                                 <div className="ds-margin-top-lg">

+                                 <Form horizontal className="ds-margin-top-lg">

                                      <Row>

                                          <Col componentClass={ControlLabel} sm={4}>

                                              Server Instance
@@ -98,9 +103,9 @@ 

                                              <input type="text" className="ds-input-auto" id="monitor-server-uptime" value={uptime} readOnly />

                                          </Col>

                                      </Row>

-                                 </div>

+                                 </Form>

                                  <hr />

-                                 <div>

+                                 <Form horizontal>

                                      <Row className="ds-margin-top">

                                          <Col componentClass={ControlLabel} sm={4}>

                                              Worker Threads
@@ -181,7 +186,7 @@ 

                                              <input type="text" className="ds-input-auto" id="monitor-server-bytessent" value={this.props.data.bytessent} readOnly />

                                          </Col>

                                      </Row>

-                                 </div>

+                                 </Form>

                              </TabPane>

                              <TabPane eventKey={2}>

                                  <ConnectionTable conns={this.props.data.connection} />
@@ -197,13 +202,15 @@ 

  ServerMonitor.propTypes = {

      serverId: PropTypes.string,

      data: PropTypes.object,

-     reload: PropTypes.func

+     reload: PropTypes.func,

+     enableTree: PropTypes.func,

  };

  

  ServerMonitor.defaultProps = {

      serverId: "",

      data: {},

-     reload: noop

+     reload: noop,

+     enableTree: noop,

  };

  

  export default ServerMonitor;

@@ -4,15 +4,20 @@ 

  import {

      Row,

      Col,

+     Form,

      Icon,

      ControlLabel,

      noop

  } from "patternfly-react";

  

  export class SNMPMonitor extends React.Component {

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      render() {

          return (

-             <div id="db-global-page">

+             <div id="snmp-page">

                  <Row>

                      <Col sm={12} className="ds-word-wrap">

                          <ControlLabel className="ds-suffix-header">
@@ -24,185 +29,185 @@ 

                          </ControlLabel>

                      </Col>

                  </Row>

-                 <div className="ds-margin-top-med">

+                 <Form horizontal className="ds-margin-top-lg">

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Anonymous Binds</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Anonymous Binds

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.anonymousbinds} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Referrals</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Referrals

                          </Col>

-                         <Col sm={2}>

+                         <Col sm={3}>

                              <input type="text" value={this.props.data.referrals} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Unauthenticated Binds</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Unauthenticated Binds

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.anonymousbinds} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Returned Referrals</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Returned Referrals

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.referralsreturned} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Simple Auth Binds</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Simple Auth Binds

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.simpleauthbinds} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Bind Security Errors</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Bind Security Errors

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.bindsecurityerrors} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Strong Auth Binds</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Strong Auth Binds

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.strongauthbinds} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Security Errors</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Security Errors

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.securityerrors} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Initiated Operations</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Initiated Operations

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.inops} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Errors</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Errors

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.errors} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Compare Operations</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Compare Operations

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.compareops} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Current Connections</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Current Connections

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.connections} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Add Operations</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Add Operations

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.addentryops} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Total Connections</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Total Connections

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.connectionseq} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Delete Operations</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Delete Operations

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.removeentryops} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Conns in Max Threads</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Conns in Max Threads

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.connectionsinmaxthreads} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Modify Operation</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Modify Operation

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.modifyentryops} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Conns Exceeded Max Threads</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Conns Exceeded Max Threads

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.connectionsmaxthreadscount} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>ModRDN Operations</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             ModRDN Operations

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.modifyrdnops} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Bytes Received</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Bytes Received

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.bytesrecv} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Search Operations</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Search Operations

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.searchops} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Bytes Sent</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Bytes Sent

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.bytessent} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>One Level Searches</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             One Level Searches

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.onelevelsearchops} size="10" readOnly />

                          </Col>

-                         <Col sm={4}>

-                             <ControlLabel>Entries Sent</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Entries Sent

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.entriesreturned} size="10" readOnly />

                          </Col>

                      </Row>

                      <Row className="ds-margin-top">

-                         <Col sm={4}>

-                             <ControlLabel>Whole Tree Searches</ControlLabel>

+                         <Col componentClass={ControlLabel} sm={3}>

+                             Whole Tree Searches

                          </Col>

                          <Col sm={2}>

                              <input type="text" value={this.props.data.wholesubtreesearchops} size="10" readOnly />

                          </Col>

  

                      </Row>

-                 </div>

+                 </Form>

              </div>

          );

      }
@@ -212,12 +217,14 @@ 

  

  SNMPMonitor.propTypes = {

      data: PropTypes.object,

-     reload: PropTypes.func

+     reload: PropTypes.func,

+     enableTree: PropTypes.func,

  };

  

  SNMPMonitor.defaultProps = {

      data: {},

-     reload: noop

+     reload: noop,

+     enableTree: noop,

  };

  

  export default SNMPMonitor;

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

      ControlLabel,

      Row,

      Col,

+     Form,

      Icon,

      Nav,

      NavItem,
@@ -26,6 +27,10 @@ 

          this.handleNavSelect = this.handleNavSelect.bind(this);

      }

  

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

      handleNavSelect(key) {

          this.setState({ activeKey: key });

      }
@@ -124,8 +129,7 @@ 

                          </ControlLabel>

                      </Col>

                  </Row>

-                 <p />

-                 <TabContainer id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>

+                 <TabContainer className="ds-margin-top-lg" id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>

                      <div>

                          <Nav bsClass="nav nav-tabs nav-tabs-pf">

                              <NavItem eventKey={1}>
@@ -159,6 +163,8 @@ 

                                              />

                                              <b>Entry Cache Hit Ratio</b>

                                          </div>

+                                         <div className="ds-divider" />

+                                         <div className="ds-divider" />

                                          <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"
@@ -184,6 +190,7 @@ 

                                                  }}

                                                  title={{type: 'pie'}}

                                                  legend={{show: true, position: 'right'}}

+                                                 unloadBeforeLoad

                                              />

                                              <b>Entry Cache Utilization</b>

                                              <div>
@@ -191,80 +198,65 @@ 

                                              </div>

                                          </div>

                                      </div>

-                                     <p />

                                      <hr />

-                                     <div>

+                                     <Form horizontal>

                                          <Row className="ds-margin-top">

-                                             <Col sm={3}>

-                                                 <ControlLabel>

-                                                     Entry Cache Hit Ratio

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 Entry Cache Hit Ratio

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 Entry Cache Tries

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 Entry Cache Hits

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 Entry Cache Max Size

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 Entry Cache Current Size

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 Entry Cache Max Entries

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 Entry Cache Count

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.currententrycachecount} size="35" readOnly />

                                              </Col>

                                          </Row>

-                                     </div>

+                                     </Form>

                                  </div>

                              </TabPane>

  
@@ -289,6 +281,8 @@ 

                                              />

                                              <b className="ds-left-margin">DN Cache Hit Ratio</b>

                                          </div>

+                                         <div className="ds-divider" />

+                                         <div className="ds-divider" />

                                          <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"
@@ -314,6 +308,7 @@ 

                                                  }}

                                                  title={{type: 'pie'}}

                                                  legend={{show: true, position: 'right'}}

+                                                 unloadBeforeLoad

                                              />

                                              <div className="ds-left-margin">

                                                  <b>DN Cache Utilization</b>
@@ -324,78 +319,64 @@ 

                                          </div>

                                      </div>

                                      <hr />

-                                     <div>

+                                     <Form horizontal>

                                          <Row className="ds-margin-top">

-                                             <Col sm={3}>

-                                                 <ControlLabel>

-                                                     DN Cache Hit Ratio

-                                                 </ControlLabel>

+                                             <Col componentClass={ControlLabel} sm={3}>

+                                                 DN Cache Hit Ratio

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 DN Cache Tries

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 DN Cache Hits

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 DN Cache Max Size

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 DN Cache Current Size

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 DN Cache Max Count

                                              </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 componentClass={ControlLabel} sm={3}>

+                                                 DN Cache Current Count

                                              </Col>

                                              <Col sm={3}>

                                                  <input type="text" value={this.props.data.currentdncachecount} size="35" readOnly />

                                              </Col>

                                          </Row>

-                                     </div>

+                                     </Form>

                                  </div>

                              </TabPane>

                          </TabContent>
@@ -411,6 +392,7 @@ 

      data: PropTypes.object,

      bename: PropTypes.string,

      reload: PropTypes.func,

+     enableTree: PropTypes.func,

  };

  

  SuffixMonitor.defaultProps = {
@@ -418,6 +400,7 @@ 

      data: {},

      bename: "",

      reload: noop,

+     enableTree: noop,

  };

  

  export default SuffixMonitor;

@@ -1,8 +1,16 @@ 

  import React from "react";

  import PropTypes from "prop-types";

  import {

+     Button,

+     Checkbox,

+     Col,

+     Form,

      Icon,

      MessageDialog,

+     Modal,

+     noop,

+     Row,

+     Spinner,

      TimedToastNotification,

      ToastNotificationList

  } from "patternfly-react";
@@ -103,4 +111,119 @@ 

      }

  }

  

+ export class DoubleConfirmModal extends React.Component {

+     render() {

+         const {

+             showModal,

+             closeHandler,

+             handleChange,

+             actionHandler,

+             checked,

+             spinning,

+             item,

+             mTitle,

+             mMsg,

+             mSpinningMsg,

+             mBtnName,

+         } = this.props;

+         let spinner = "";

+         let saveDisabled = true;

+ 

+         if (spinning) {

+             spinner =

+                 <Row>

+                     <div className="ds-margin-top ds-modal-spinner">

+                         <Spinner loading inline size="md" />{mSpinningMsg}

+                     </div>

+                 </Row>;

+             saveDisabled = true;

+         }

+         if (checked) {

+             saveDisabled = false;

+         }

+ 

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

+                             {mTitle}

+                         </Modal.Title>

+                     </Modal.Header>

+                     <Modal.Body>

+                         <Form horizontal autoComplete="off">

+                             <h4>{mMsg}</h4>

+                             <h5 className="ds-center ds-margin-top-xlg"><b>{item}</b></h5>

+                             <Row className="ds-margin-top-xlg">

+                                 <Col sm={12} className="ds-center">

+                                     <Checkbox

+                                         id="modalChecked"

+                                         defaultChecked={checked}

+                                         onChange={handleChange}

+                                     >

+                                         <b>Yes</b>, I am sure.

+                                     </Checkbox>

+                                 </Col>

+                             </Row>

+                             {spinner}

+                         </Form>

+                     </Modal.Body>

+                     <Modal.Footer>

+                         <Button

+                             bsStyle="default"

+                             className="btn-cancel"

+                             onClick={closeHandler}

+                         >

+                             Cancel

+                         </Button>

+                         <Button

+                             bsStyle="primary"

+                             onClick={actionHandler}

+                             disabled={saveDisabled}

+                         >

+                             {mBtnName}

+                         </Button>

+                     </Modal.Footer>

+                 </div>

+             </Modal>

+         );

+     }

+ }

+ 

+ DoubleConfirmModal.propTypes = {

+     showModal: PropTypes.bool,

+     closeHandler: PropTypes.func,

+     handleChange: PropTypes.func,

+     actionHandler: PropTypes.func,

+     spinning: PropTypes.bool,

+     item: PropTypes.string,

+     checked: PropTypes.bool,

+     mTitle: PropTypes.string,

+     mMsg: PropTypes.string,

+     mSpinningMsg: PropTypes.string,

+     mBtnName: PropTypes.string,

+ };

+ 

+ DoubleConfirmModal.defaultProps = {

+     showModal: false,

+     closeHandler: noop,

+     handleChange: noop,

+     actionHandler: noop,

+     spinning: false,

+     item: "",

+     checked: false,

+     mTitle: "",

+     mMsg: "",

+     mSpinningMsg: "",

+     mBtnName: "",

+ };

+ 

  export { NotificationController, ConfirmPopup };

@@ -216,8 +216,9 @@ 

                          )}

                      </Row>

                  </Form>

-                 <p />

-                 {this.props.children}

+                 <div className="ds-margin-top">

+                     {this.props.children}

+                 </div>

                  <CustomCollapse>

                      <Row>

                          <Col sm={12}>

@@ -0,0 +1,395 @@ 

+ import cockpit from "cockpit";

+ import React from "react";

+ import "../../css/ds.css";

+ import PropTypes from "prop-types";

+ import { ConfirmPopup } from "../notifications.jsx";

+ import { log_cmd } from "../tools.jsx";

+ import {

+     noop,

+     Row,

+     Button,

+     Col,

+     ControlLabel,

+     Checkbox,

+     Form,

+     Icon,

+     Spinner,

+ } from "patternfly-react";

+ 

+ export class Changelog extends React.Component {

+     constructor (props) {

+         super(props);

+         this.state = {

+             loading: false,

+             errObj: {},

+             showConfirmDelete: false,

+             // Changelog settings

+             clDir: this.props.clDir,

+             clMaxEntries: this.props.clMaxEntries,

+             clMaxAge: this.props.clMaxAge,

+             clCompactInt: this.props.clCompactInt,

+             clTrimInt: this.props.clTrimInt,

+             clEncrypt: this.props.clEncrypt,

+             // Preserve original settings

+             _clDir: this.props.clDir,

+             _clMaxEntries: this.props.clMaxEntries,

+             _clMaxAge: this.props.clMaxAge,

+             _clCompactInt: this.props.clCompactInt,

+             _clTrimInt: this.props.clTrimInt,

+             _clEncrypt: this.props.clEncrypt,

+         };

+ 

+         this.handleChange = this.handleChange.bind(this);

+         this.saveSettings = this.saveSettings.bind(this);

+         this.createChangelog = this.createChangelog.bind(this);

+         this.confirmChangelogDelete = this.confirmChangelogDelete.bind(this);

+         this.closeConfirmDelete = this.closeConfirmDelete.bind(this);

+         this.deleteChangelog = this.deleteChangelog.bind(this);

+     }

+ 

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

+     createChangelog () {

+         this.setState({

+             saving: true

+         });

+         let cmd = [

+             'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',

+             'replication', 'create-changelog'

+         ];

+         cockpit

+                 .spawn(cmd, {superuser: true, "err": "message"})

+                 .done(content => {

+                     this.props.reload();

+                     this.props.addNotification(

+                         "success",

+                         "Successfully created replication changelog"

+                     );

+                     this.setState({

+                         saving: false

+                     });

+                 })

+                 .fail(err => {

+                     let errMsg = JSON.parse(err);

+                     this.props.reload();

+                     this.setState({

+                         saving: false

+                     });

+                     let msg = errMsg.desc;

+                     if ('info' in errMsg) {

+                         msg = errMsg.desc + " - " + errMsg.info;

+                     }

+                     this.props.addNotification(

+                         "error",

+                         `Error creating changelog - ${msg}`

+                     );

+                 });

+     }

+ 

+     confirmChangelogDelete () {

+         this.setState({

+             showConfirmDelete: true

+         });

+     }

+ 

+     closeConfirmDelete () {

+         this.setState({

+             showConfirmDelete: false

+         });

+     }

+ 

+     deleteChangelog () {

+         this.setState({

+             saving: true

+         });

+         let cmd = ['dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',

+             'replication', 'delete-changelog'

+         ];

+         cockpit

+                 .spawn(cmd, {superuser: true, "err": "message"})

+                 .done(content => {

+                     this.props.reload();

+                     this.props.addNotification(

+                         "success",

+                         "Successfully deleted replication changelog"

+                     );

+                     this.setState({

+                         saving: false

+                     });

+                 })

+                 .fail(err => {

+                     let errMsg = JSON.parse(err);

+                     this.props.reload();

+                     this.setState({

+                         saving: false

+                     });

+                     let msg = errMsg.desc;

+                     if ('info' in errMsg) {

+                         msg = errMsg.desc + " - " + errMsg.info;

+                     }

+                     this.props.addNotification(

+                         "error",

+                         `Error deleting changelog - ${msg}`

+                     );

+                 });

+     }

+ 

+     saveSettings () {

+         let cmd = [

+             'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',

+             'replication', 'set-changelog'

+         ];

+         if (this.state.clDir != this.state._clDir) {

+             if (this.state.clDir == "") {

+                 // Changelog directory can not be empty

+                 let errObj = this.state.errObj;

+                 errObj["clDir"] = true;

+                 this.setState({

+                     errObj: errObj

+                 });

+                 return;

+             }

+             cmd.push("--cl-dir =" + this.state.clDir);

+         }

+         if (this.state.clMaxEntries != this.state._clMaxEntries) {

+             cmd.push("--max-entries=" + this.state.clMaxEntries);

+         }

+         if (this.state.clMaxAge != this.state._clMaxAge) {

+             cmd.push("--max-age=" + this.state.clMaxAge);

+         }

+         if (this.state.clCompactInt != this.state._clCompactInt) {

+             cmd.push("--compact-interval=" + this.state.clCompactInt);

+         }

+         if (this.state.clTrimInt != this.state._clTrimInt) {

+             cmd.push("--trim-interval=" + this.state.clTrimInt);

+         }

+         if (this.state.clEncrypt != this.state._clEncrypt) {

+             // TODO - Not implemented in dsconf yet

+             // cmd.push("--encrypt=" + this.state.clEncrypt);

+         }

+         if (cmd.length > 5) {

+             this.setState({

+                 // Start the spinner

+                 saving: true

+             });

+             log_cmd("saveSettings", "Applying replication changelog changes", cmd);

+             let msg = "Successfully updated changelog configuration.";

+             cockpit

+                     .spawn(cmd, {superuser: true, "err": "message"})

+                     .done(content => {

+                         this.props.reload();

+                         this.props.addNotification(

+                             "success",

+                             msg

+                         );

+                         this.setState({

+                             saving: false

+                         });

+                     })

+                     .fail(err => {

+                         let errMsg = JSON.parse(err);

+                         this.props.reload();

+                         this.setState({

+                             saving: false

+                         });

+                         let msg = errMsg.desc;

+                         if ('info' in errMsg) {

+                             msg = errMsg.desc + " - " + errMsg.info;

+                         }

+                         this.props.addNotification(

+                             "error",

+                             `Error updating changelog configuration - ${msg}`

+                         );

+                     });

+         }

+     }

+ 

+     handleChange(e) {

+         const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;

+         let valueErr = false;

+         let errObj = this.state.errObj;

+         if (e.target.id == "clDir" && value == "") {

+             valueErr = true;

+         }

+         errObj[e.target.id] = valueErr;

+         this.setState({

+             [e.target.id]: value,

+             errObj: errObj

+         });

+     }

+ 

+     render() {

+         let clPage;

+         if (this.state._clDir == "") {

+             // No changelog, only show clDir and Create button

+             clPage =

+                 <div>

+                     <Row>

+                         <Col sm={12} className="ds-word-wrap">

+                             <ControlLabel className="ds-suffix-header">

+                                 Replication Changelog

+                                 <Icon className="ds-left-margin ds-refresh"

+                                     type="fa" name="refresh" title="Refresh changelog settings"

+                                     onClick={this.props.reload}

+                                 />

+                             </ControlLabel>

+                         </Col>

+                     </Row>

+                     <hr />

+                     <div className="ds-margin-top-med ds-center">

+                         <p>There is no Replication Changelog</p>

+                         <Row className="ds-margin-top-lg" title="Create the replication changelog">

+                             <Button

+                                 bsStyle="primary"

+                                 onClick={this.createChangelog}

+                             >

+                                 Create Changelog

+                             </Button>

+                         </Row>

+                     </div>

+                 </div>;

+         } else if (this.state.saving) {

+             clPage =

+                 <div className="ds-margin-top ds-loading-spinner ds-center">

+                     <h4>Saving changelog configuration ...</h4>

+                     <Spinner className="ds-margin-top-lg" loading size="md" />

+                 </div>;

+         } else if (this.props.loading) {

+             clPage =

+                 <div className="ds-loading-spinner ds-center">

+                     <h4>Loading changelog configuration ...</h4>

+                     <Spinner className="ds-margin-top-lg" loading size="md" />

+                 </div>;

+         } else {

+             clPage =

+                 <div>

+                     <Row>

+                         <Col sm={5} className="ds-word-wrap">

+                             <h4>

+                                 Replication Changelog

+                                 <Icon className="ds-left-margin ds-refresh"

+                                     type="fa" name="refresh" title="Refresh changelog settings"

+                                     onClick={this.props.reload}

+                                 />

+                             </h4>

+                         </Col>

+                         <Col sm={7}>

+                             <Button

+                                 className="ds-float-right"

+                                 bsStyle="danger"

+                                 onClick={this.confirmChangelogDelete}

+                             >

+                                 Delete Changelog

+                             </Button>

+                         </Col>

+                     </Row>

+                     <Form horizontal>

+                         <hr />

+                         <Row className="ds-margin-top" title="The filesystem location of the replication changelog database">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Changelog Directory

+                             </Col>

+                             <Col sm={8}>

+                                 <input value={this.state.clDir} id="clDir" onChange={this.handleChange} className={this.state.errObj.clDir ? "ds-input-auto-bad" : "ds-input-auto"} />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="Changelog trimming parameter.  Set the maximum number of changelog entries allowed in the database (nsslapd-changelogmaxentries).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Changelog Maximum Entries

+                             </Col>

+                             <Col sm={8}>

+                                 <input value={this.state.clMaxEntries} id="clMaxEntries" onChange={this.handleChange} className="ds-input-auto" />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="Changelog trimming parameter.  This set the maximum age of a changelog entry.  It is recommended to use the same value as the Replication Purge Delay.  (nsslapd-changelogmaxage).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Changelog Maximum Age

+                             </Col>

+                             <Col sm={8}>

+                                 <input value={this.state.clMaxAge} id="clMaxAge" onChange={this.handleChange} className="ds-input-auto" />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The changelog trimming interval.  Set how often the changelog checks if there are entries that can be purged from the changelog based on the trimming parameters (nsslapd-changelogtrim-interval).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Changelog Trimming Interval

+                             </Col>

+                             <Col sm={8}>

+                                 <input value={this.state.clTrimInt} id="clTrimInt" onChange={this.handleChange} className="ds-input-auto" />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top" title="The changelog compaction interval.  Set how often the changelog will compact itself, meaning remove empty/trimmed database slots.  The default is 30 days. (nsslapd-changelogcompactdb-interval).">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Changelog Compaction Interval

+                             </Col>

+                             <Col sm={8}>

+                                 <input value={this.state.clCompactInt} id="clCompactInt" onChange={this.handleChange} className="ds-input-auto" />

+                             </Col>

+                         </Row>

+                         <Row hidden className="ds-margin-top" title="TLS must first be enabled in the server for change encryption to work.  Please consult Administration Guide for more details.">

+                             <Col componentClass={ControlLabel} sm={4}>

+                                 Changelog Encryption

+                             </Col>

+                             <Col sm={2}>

+                                 <Checkbox

+                                     id="clEncrypt"

+                                     checked={this.state.clEncrypt}

+                                     onChange={this.handleChange}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top-lg">

+                             <Col sm={2}>

+                                 <Button

+                                     bsStyle="primary"

+                                     onClick={this.saveSettings}

+                                 >

+                                     Save

+                                 </Button>

+                             </Col>

+                         </Row>

+                     </Form>

+                 </div>;

+         }

+ 

+         return (

+             <div>

+                 {clPage}

+                 <ConfirmPopup

+                     showModal={this.state.showConfirmDelete}

+                     closeHandler={this.closeConfirmDelete}

+                     actionFunc={this.deleteChangelog}

+                     msg="Are you sure you want to delete the changelog?"

+                     msgContent="This will invalidate all replication agreements, and they will need to be reinitialized."

+                 />

+             </div>

+         );

+     }

+ }

+ 

+ Changelog.propTypes = {

+     serverId: PropTypes.string,

+     clDir: PropTypes.string,

+     clMaxEntries: PropTypes.string,

+     clMaxAge: PropTypes.string,

+     clCompactInt: PropTypes.string,

+     clTrimInt: PropTypes.string,

+     clEncrypt: PropTypes.bool,

+     addNotification: PropTypes.func,

+     reload: PropTypes.func,

+     enableTree: PropTypes.func,

+ };

+ 

+ Changelog.defaultProps = {

+     serverId: "",

+     clDir: "",

+     clMaxEntries: "",

+     clMaxAge: "",

+     clCompactInt: "",

+     clTrimInt: "",

+     clEncrypt: false,

+     addNotification: noop,

+     reload: noop,

+     enableTree: noop,

+ };

@@ -0,0 +1,640 @@ 

+ import cockpit from "cockpit";

+ import React from "react";

+ import { log_cmd, valid_dn } from "../tools.jsx";

+ import { ConfirmPopup } from "../notifications.jsx";

+ import CustomCollapse from "../customCollapse.jsx";

+ import { ManagerTable } from "./replTables.jsx";

+ import { AddManagerModal, ChangeReplRoleModal } from "./replModals.jsx";

+ import {

+     Button,

+     Row,

+     Checkbox,

+     Col,

+     ControlLabel,

+     Form,

+     FormControl,

+     Spinner,

+     // noop,

+ } from "patternfly-react";

+ // import PropTypes from "prop-types";

+ import "../../css/ds.css";

+ 

+ export class ReplConfig extends React.Component {

+     constructor(props) {

+         super(props);

+         this.state = {

+             saving: false,

+             showConfirmManagerDelete: false,

+             showAddManagerModal: false,

+             showPromoteDemoteModal: false,

+             addManagerSpinning: false,

+             roleChangeSpinning: false,

+             manager: "cn=replication manager,cn=config",

+             manager_passwd: "",

+             manager_passwd_confirm: "",

+             newRole: this.props.role == "Master" ? "Hub" : "Master",

+             newRID: "1",

+             modalChecked: false,

+             errObj: {},

+             // Config Settings

+             nsds5replicabinddn: this.props.data['nsds5replicabinddn'],

+             nsds5replicabinddngroup: this.props.data['nsds5replicabinddngroup'],

+             nsds5replicabinddngroupcheckinterval: this.props.data['nsds5replicabinddngroupcheckinterval'],

+             nsds5replicareleasetimeout: this.props.data['nsds5replicareleasetimeout'],

+             nsds5replicapurgedelay: this.props.data['nsds5replicapurgedelay'],

+             nsds5replicatombstonepurgeinterval: this.props.data['nsds5replicatombstonepurgeinterval'],

+             nsds5replicaprecisetombstonepurging: this.props.data['nsds5replicaprecisetombstonepurging'],

+             nsds5replicaprotocoltimeout: this.props.data['nsds5replicaprotocoltimeout'],

+             nsds5replicabackoffmin: this.props.data['nsds5replicabackoffmin'],

+             nsds5replicabackoffmax: this.props.data['nsds5replicabackoffmax'],

+             // Original settings

+             _nsds5replicabinddn: this.props.data['nsds5replicabinddn'],

+             _nsds5replicabinddngroup: this.props.data['nsds5replicabinddngroup'],

+             _nsds5replicabinddngroupcheckinterval: this.props.data['nsds5replicabinddngroupcheckinterval'],

+             _nsds5replicareleasetimeout: this.props.data['nsds5replicareleasetimeout'],

+             _nsds5replicapurgedelay: this.props.data['nsds5replicapurgedelay'],

+             _nsds5replicatombstonepurgeinterval: this.props.data['nsds5replicatombstonepurgeinterval'],

+             _nsds5replicaprecisetombstonepurging: this.props.data['nsds5replicaprecisetombstonepurging'],

+             _nsds5replicaprotocoltimeout: this.props.data['nsds5replicaprotocoltimeout'],

+             _nsds5replicabackoffmin: this.props.data['nsds5replicabackoffmin'],

+             _nsds5replicabackoffmax: this.props.data['nsds5replicabackoffmax'],

+ 

+         };

+ 

+         this.confirmManagerDelete = this.confirmManagerDelete.bind(this);

+         this.closeConfirmManagerDelete = this.closeConfirmManagerDelete.bind(this);

+         this.deleteManager = this.deleteManager.bind(this);

+         this.showAddManager = this.showAddManager.bind(this);

+         this.closeAddManagerModal = this.closeAddManagerModal.bind(this);

+         this.addManager = this.addManager.bind(this);

+         this.handleChange = this.handleChange.bind(this);

+         this.handleManagerChange = this.handleManagerChange.bind(this);

+         this.showPromoteDemoteModal = this.showPromoteDemoteModal.bind(this);

+         this.closePromoteDemoteModal = this.closePromoteDemoteModal.bind(this);

+         this.doRoleChange = this.doRoleChange.bind(this);

+         this.saveConfig = this.saveConfig.bind(this);

+     }

+ 

+     doRoleChange (changeType) {

+         let action = "demote";

+         if (changeType == "Promoting") {

+             action = "promote";

+         }

+         let cmd = ['dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket', 'replication', action,

+             '--suffix=' + this.props.suffix, "--newrole=" + this.state.newRole];

+         if (this.state.newRole == "Master") {

+             let ridNum = parseInt(this.state.newRID, 10);

+             if (ridNum < 1 || ridNum >= 65535) {

+                 this.props.addNotification(

+                     "error",

+                     "A Master replica requires a unique numerical identifier.  Please enter an ID between 1 and 65534"

+                 );

+                 return;

+             }

+             cmd.push("--replica-id=" + this.state.newRID);

+         }

+         this.setState({

+             roleChangeSpinning: true

+         });

+         log_cmd('doRoleChange', 'change replica role', cmd);

+         cockpit

+                 .spawn(cmd, { superuser: true, err: "message" })

+                 .done(content => {

+                     this.props.reload();

+                     this.props.addNotification(

+                         "success",

+                         `Successfully ${action}d replica to a ${this.state.newRole}`

+                     );

+                     this.setState({

+                         roleChangeSpinning: false,

+                         showPromoteDemoteModal: false

+                     });

+                 })

+                 .fail(err => {

+                     let errMsg = JSON.parse(err);

+                     this.props.reload();

+                     this.props.addNotification(

+                         "error",

+                         `Failed to ${action} replica - ${errMsg.desc}`

+                     );

+                     this.setState({

+                         roleChangeSpinning: false,

+                         showPromoteDemoteModal: false

+                     });

+                 });

+     }

+ 

+     closePromoteDemoteModal () {

+         this.setState({

+             showPromoteDemoteModal: false

+         });

+     }

+ 

+     showPromoteDemoteModal () {

+         this.setState({

+             showPromoteDemoteModal: true,

+             modalChecked: false,

+         });

+     }

+ 

+     closeAddManagerModal () {

+         this.setState({

+             showAddManagerModal: false

+         });

+     }

+ 

+     showAddManager () {

+         this.setState({

+             showAddManagerModal: true,

+             manager: "cn=replication manager,cn=config",

+             manager_passwd: "",

+             manager_passwd_confirm: "",

+             errObj: {

+                 manager_passwd: true,

+                 manager_passwd_confirm: true,

+             }

+         });

+     }

+ 

+     addManager () {

+         // Validate DN

+         if (!valid_dn(this.state.manager)) {

+             this.props.addNotification(

+                 "error",

+                 `Invalid DN for the Replication Manager: ${this.state.manager}`

+             );

+             return;

+         }

+ 

+         if (this.state.manager_passwd == "" || this.state.manager_passwd_confirm == "") {

+             this.props.addNotification(

+                 "error", "You must provide a password for the Replication Manager"

+             );

+             return;

+         }

+         if (this.state.manager_passwd != this.state.manager_passwd_confirm) {

+             this.props.addNotification(

+                 "error", "Passwords do not match"

+             );

+             return;

+         }

+ 

+         this.setState({

+             addManagerSpinning: true

+         });

+ 

+         let cmd = [

+             "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",

+             "replication", "create-manager", "--suffix=" + this.props.suffix, "--name=" + this.state.manager,

+             "--passwd=" + this.state.manager_passwd

+         ];

+ 

+         log_cmd("addManager", "Adding Replication Manager", cmd);

+         cockpit

+                 .spawn(cmd, { superuser: true, err: "message" })

+                 .done(content => {

+                     this.props.reloadConfig(this.props.suffix);

+                     this.props.addNotification(

+                         "success",

+                         `Successfully added Replication Manager`

+                     );

+                     this.setState({

+                         addManagerSpinning: false,

+                         showAddManagerModal: false

+                     });

+                 })

+                 .fail(err => {

+                     let errMsg = JSON.parse(err);

+                     this.props.reloadConfig(this.props.suffix);

+                     this.props.addNotification(

+                         "error",

+                         `Failure adding Replication Manager - ${errMsg.desc}`

+                     );

+                     this.setState({

+                         addManagerSpinning: false,

+                         showAddManagerModal: false

+                     });

+                 });

+     }

+ 

+     handleChange(e) {

+         let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;

+         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

+         });

+     }

+ 

+     handleManagerChange(e) {

+         let value = e.target.value;

+         let attr = e.target.id;

+         let valueErr = false;

+         let errObj = this.state.errObj;

+         if (value == "") {

+             valueErr = true;

+         }

+         // Handle password chnages

+         if (attr == "manager_passwd") {

+             if (value != this.state.manager_passwd_confirm) {

+                 // No match

+                 valueErr = true;

+             } else {

+                 errObj[attr] = false;

+                 errObj['manager_passwd_confirm'] = false;

+             }

+         } else if (attr == "manager_passwd_confirm") {

+             if (value != this.state.manager_passwd) {

+                 // No match

+                 valueErr = true;

+             } else {

+                 errObj[attr] = false;

+                 errObj['manager_passwd'] = false;

+             }

+         }

+ 

+         errObj[attr] = valueErr;

+         this.setState({

+             [attr]: value,

+             errObj: errObj

+         });

+     }

+ 

+     confirmManagerDelete (item) {

+         this.setState({

+             showConfirmManagerDelete: true,

+             manager: item.name,

+         });

+     }

+ 

+     closeConfirmManagerDelete () {

+         this.setState({

+             showConfirmManagerDelete: false,

+             manager: "",

+         });

+     }

+ 

+     deleteManager (dn) {

+         let cmd = [

+             "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",

+             "replication", "delete-manager", "--suffix=" + this.props.suffix, "--name=" + dn

+         ];

+         log_cmd("deleteManager", "Deleting Replication Manager", cmd);

+         cockpit

+                 .spawn(cmd, { superuser: true, err: "message" })

+                 .done(content => {

+                     this.props.reloadConfig(this.props.suffix);

+                     this.props.addNotification(

+                         "success",

+                         `Successfully removed Replication Manager`

+                     );

+                 })

+                 .fail(err => {

+                     let errMsg = JSON.parse(err);

+                     this.props.reloadConfig(this.props.suffix);

+                     this.props.addNotification(

+                         "error",

+                         `Failure removing Replication Manager - ${errMsg.desc}`

+                     );

+                 });

+     }

+ 

+     saveConfig () {

+         let cmd = [

+             'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',

+             'replication', 'set', '--suffix=' + this.props.suffix

+         ];

+ 

+         if (this.state.nsds5replicabackoffmax != this.state._nsds5replicabackoffmax) {

+             cmd.push("--repl-backoff-max=" + this.state.nsds5replicabackoffmax);

+         }

+         if (this.state.nsds5replicabackoffmin != this.state._nsds5replicabackoffmin) {

+             cmd.push("--repl-backoff-min=" + this.state.nsds5replicabackoffmin);

+         }

+ 

+         if (this.state.nsds5replicaprotocoltimeout != this.state._nsds5replicaprotocoltimeout) {

+             cmd.push("--repl-protocol-timeout=" + this.state.nsds5replicaprotocoltimeout);

+         }

+         if (this.state.nsds5replicaprecisetombstonepurging != this.state._nsds5replicaprecisetombstonepurging) {

+             if (this.state.nsds5replicaprecisetombstonepurging) {

+                 cmd.push("--repl-fast-tombstone-purging=on");

+             } else {

+                 cmd.push("--repl-fast-tombstone-purging=off");

+             }

+         }

+         if (this.state.nsds5replicatombstonepurgeinterval != this.state._nsds5replicatombstonepurgeinterval) {

+             cmd.push("--repl-tombstone-purge-interval=" + this.state.nsds5replicatombstonepurgeinterval);

+         }

+         if (this.state.nsds5replicabinddngroup != this.state._nsds5replicabinddngroup) {

+             cmd.push("--repl-bind-group=" + this.state.nsds5replicabinddngroup);

+         }

+         if (this.state.nsds5replicabinddngroupcheckinterval != this.state._nsds5replicabinddngroupcheckinterval) {

+             cmd.push("--repl-bind-group-interval=" + this.state.nsds5replicabinddngroupcheckinterval);

+         }

+         if (this.state.nsds5replicareleasetimeout != this.state._nsds5replicareleasetimeout) {

+             cmd.push("--repl-release-timeout=" + this.state.nsds5replicareleasetimeout);

+         }

+         if (this.state.nsds5replicapurgedelay != this.state._nsds5replicapurgedelay) {

+             cmd.push("--repl-purge-delay=" + this.state.nsds5replicapurgedelay);

+         }

+         if (cmd.length > 6) {

+             this.setState({

+                 // Start the spinner

+                 saving: true

+             });

+             log_cmd("saveConfig", "Applying replication changes", cmd);

+             let msg = "Successfully updated replication configuration.";

+             cockpit

+                     .spawn(cmd, {superuser: true, "err": "message"})

+                     .done(content => {

+                         this.props.reloadConfig(this.props.suffix);

+                         this.props.addNotification(

+                             "success",

+                             msg

+                         );

+                         this.setState({

+                             saving: false

+                         });

+                     })

+                     .fail(err => {

+                         let errMsg = JSON.parse(err);

+                         this.props.reloadConfig(this.props.suffix);

+                         this.setState({

+                             saving: false

+                         });

+                         let msg = errMsg.desc;

+                         if ('info' in errMsg) {

+                             msg = errMsg.desc + " - " + errMsg.info;

+                         }

+                         this.props.addNotification(

+                             "error",

+                             `Error updating replication configuration - ${msg}`

+                         );

+                     });

+         }

+     }

+ 

+     render() {

+         let content = "";

+         let roleButton = "";

+         let manager_rows = [];

+         for (let row of this.props.data.nsds5replicabinddn) {

+             manager_rows.push({'name': row});

+         }

+ 

+         if (this.props.role == "Master") {

+             roleButton =

+                 <Button

+                     bsStyle="primary"

+                     onClick={this.showPromoteDemoteModal}

+                     title="Demote this Master replica to a Hub or Consumer"

+                     className="ds-inline-btn"

+                 >

+                     Demote

+                 </Button>;

+         } else if (this.props.role == "Hub") {

+             roleButton =

+                 <Button

+                     bsStyle="primary"

+                     onClick={this.showPromoteDemoteModal}

+                     title="Promote or Demote this Hub replica to a Master or Consumer"

+                     className="ds-inline-btn"

+                 >

+                     Promote/Demote

+                 </Button>;

+         } else {

+             // Consumer

+             roleButton =

+                 <Button

+                     bsStyle="primary"

+                     onClick={this.showPromoteDemoteModal}

+                     title="Promte this Consumer replica to a Master or Hub"

+                     className="ds-inline-btn"

+                 >

+                     Promote

+                 </Button>;

+         }

+ 

+         if (this.state.saving) {

+             content =

+                 <div className="ds-margin-top-xxlg ds-loading-spinner-tree ds-center">

+                     <h4>Saving replication configuration ...</h4>

+                     <Spinner loading size="md" />

+                 </div>;

+         } else {

+             content =

+                 <div className="ds-margin-top-xxlg ds-left-margin">

+                     <Form horizontal>

+                         <Row className="ds-margin-top-xlg">

+                             <Col sm={2}>

+                                 <ControlLabel>

+                                     Replica Role

+                                 </ControlLabel>

+                             </Col>

+                             <Col sm={6}>

+                                 <input type="text" defaultValue={this.props.role} size="10" disabled />{roleButton}

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top">

+                             <Col sm={2}>

+                                 <ControlLabel>

+                                     Replica ID

+                                 </ControlLabel>

+                             </Col>

+                             <Col sm={4}>

+                                 <input type="text" defaultValue={this.props.data.nsds5replicaid} size="10" disabled />

+                             </Col>

+                         </Row>

+                         <hr />

+                         <Row className="ds-margin-top">

+                             <Col sm={9}>

+                                 <ManagerTable

+                                     rows={manager_rows}

+                                     confirmDelete={this.confirmManagerDelete}

+                                 />

+                             </Col>

+                         </Row>

+                         <Row className="ds-margin-top">

+                             <Col sm={4}>

+                                 <Button

+                                     bsStyle="primary"

+                                     onClick={this.showAddManager}

+                                 >

+                                     Add Replication Manager

+                                 </Button>

+                             </Col>

+                         </Row>

+                         <CustomCollapse>

+                             <div className="ds-margin-top">

+                                 <div className="ds-margin-left">

+                                     <Row className="ds-margin-top-lg" title="The DN of the replication manager">

+                                         <Col componentClass={ControlLabel} sm={4}>

+                                             Bind DN Group

+                                         </Col>

+                                         <Col sm={6}>

+                                             <FormControl

+                                                 id="nsds5replicabinddngroup"

+                                                 type="text"

+                                                 defaultValue={this.state.nsds5replicabinddngroup}

+                                                 onChange={this.handleChange}

+                                             />

+                                         </Col>

+                                     </Row>

+                                     <Row className="ds-margin-top" title="The interval to check for any changes in the group memebrship specified in the Bind DN Group and automatically rebuilds the list for the replication managers accordingly.  (nsds5replicabinddngroupcheckinterval).">

+                                         <Col componentClass={ControlLabel} sm={4}>

+                                             Bind DN Group Check Interval

+                                         </Col>

+                                         <Col sm={6}>

+                                             <FormControl

+                                                 id="nsds5replicabinddngroupcheckinterval"

+                                                 type="text"

+                                                 defaultValue={this.state.nsds5replicabinddngroupcheckinterval}

+                                                 onChange={this.handleChange}

+                                             />

+                                         </Col>

+                                     </Row>

+                                     <Row className="ds-margin-top" title="This controls the maximum age of deleted entries (tombstone entries), and entry state information.  (nsds5replicapurgedelay).">

+                                         <Col componentClass={ControlLabel} sm={4}>

+                                             Purge Delay

+                                         </Col>

+                                         <Col sm={6}>

+                                             <FormControl

+                                                 id="nsds5replicapurgedelay"

+                                                 type="text"

+                                                 defaultValue={this.state.nsds5replicapurgedelay}

+                                                 onChange={this.handleChange}

+                                             />

+                                         </Col>

+                                     </Row>

+                                     <Row className="ds-margin-top" title="This attribute specifies the time interval in seconds between purge operation cycles.  (nsds5replicatombstonepurgeinterval).">

+                                         <Col componentClass={ControlLabel} sm={4}>

+                                             Tombstone Purge Interval

+                                         </Col>

+                                         <Col sm={6}>

+                                             <FormControl

+                                                 id="nsds5replicatombstonepurgeinterval"

+                                                 type="text"

+                                                 defaultValue={this.state.nsds5replicatombstonepurgeinterval}

+                                                 onChange={this.handleChange}

+                                             />

+                                         </Col>

+                                     </Row>

+                                     <Row className="ds-margin-top" title="A time limit (in seconds) that tells a replication session to yield if other replicas are trying to acquire this one (nsds5replicareleasetimeout).">

+                                         <Col componentClass={ControlLabel} sm={4}>

+                                             Replica Release Timeout

+                                         </Col>

+                                         <Col sm={6}>

+                                             <FormControl

+                                                 id="nsds5replicareleasetimeout"

+                                                 type="text"

+                                                 defaultValue={this.state.nsds5replicareleasetimeout}

+                                                 onChange={this.handleChange}

+                                             />

+                                         </Col>

+                                     </Row>

+                                     <Row className="ds-margin-top" title="A timeout on how long to wait before stopping a replication session when the server is being stopped, replication is being disabled, or when removing a replication agreement. (nsds5replicaprotocoltimeout).">

+                                         <Col componentClass={ControlLabel} sm={4}>

+                                             Replication Timeout

+                                         </Col>

+                                         <Col sm={6}>

+                                             <FormControl

+                                                 id="nsds5replicaprotocoltimeout"

+                                                 type="text"

+                                                 defaultValue={this.state.nsds5replicaprotocoltimeout}

+                                                 onChange={this.handleChange}

+                                             />

+                                         </Col>

+                                     </Row>

+                                     <Row className="ds-margin-top" title="This is the minimum amount of time in seconds that a replication will go into a backoff state  (nsds5replicabackoffmin).">

+                                         <Col componentClass={ControlLabel} sm={4}>

+                                             Back Off Minimum

+                                         </Col>

+                                         <Col sm={6}>

+                                             <FormControl

+                                                 id="nsds5replicabackoffmin"

+                                                 type="text"

+                                                 defaultValue={this.state.nsds5replicabackoffmin}

+                                                 onChange={this.handleChange}

+                                             />

+                                         </Col>

+                                     </Row>

+                                     <Row className="ds-margin-top" title="This is the maximum amount of time in seconds that a replication will go into a backoff state  (nsds5replicabackoffmax).">

+                                         <Col componentClass={ControlLabel} sm={4}>

+                                             Back Off Maximum

+                                         </Col>

+                                         <Col sm={6}>

+                                             <FormControl

+                                                 id="nsds5replicabackoffmax"

+                                                 type="text"

+                                                 defaultValue={this.state.nsds5replicabackoffmax}

+                                                 onChange={this.handleChange}

+                                             />

+                                         </Col>

+                                     </Row>

+                                     <Row className="ds-margin-top" title="Enables faster tombstone purging (nsds5replicaprecisetombstonepurging)">

+                                         <Col componentClass={ControlLabel} sm={4}>

+                                             Fast Tombstone Purging

+                                         </Col>

+                                         <Col sm={6}>

+                                             <Checkbox

+                                                 id="nsds5replicaprecisetombstonepurging"

+                                                 defaultChecked={this.props.data.nsds5replicaprecisetombstonepurging}

+                                                 onChange={this.handleChange}

+                                             />

+                                         </Col>

+                                     </Row>

+                                     <Row className="ds-margin-top-lg">

+                                         <Col componentClass={ControlLabel} sm={4}>

+                                             <Button

+                                                 bsStyle="primary"

+                                                 onClick={this.saveConfig}

+                                             >

+                                                 Save Configuration

+                                             </Button>

+                                         </Col>

+                                     </Row>

+                                 </div>

+                             </div>

+                         </CustomCollapse>

+                     </Form>

+                     <ConfirmPopup

+                         showModal={this.state.showConfirmManagerDelete}

+                         closeHandler={this.closeConfirmManagerDelete}

+                         actionFunc={this.deleteManager}

+                         actionParam={this.state.manager}

+                         msg="Are you sure you want to remove this Replication Manager?"

+                         msgContent={this.state.manager}

+                     />

+                     <AddManagerModal

+                         showModal={this.state.showAddManagerModal}

+                         closeHandler={this.closeAddManagerModal}

+                         handleChange={this.handleManagerChange}

+                         saveHandler={this.addManager}

+                         spinning={this.state.addManagerSpinning}

+                         error={this.state.errObj}

+                     />

+                     <ChangeReplRoleModal

+                         showModal={this.state.showPromoteDemoteModal}

+                         closeHandler={this.closePromoteDemoteModal}

+                         handleChange={this.handleChange}

+                         saveHandler={this.doRoleChange}

+                         spinning={this.state.roleChangeSpinning}

+                         role={this.props.role}

+                         newRole={this.state.newRole}

+                         checked={this.state.modalChecked}

+                     />

+                 </div>;

+         }

+ 

+         return (

+             <div>

+                 {content}

+             </div>

+         );

+     }

+ }

@@ -0,0 +1,456 @@ 

+ import React from "react";

+ import cockpit from "cockpit";

+ import { ReplConfig } from "./replConfig.jsx";

+ import { WinsyncAgmts } from "./winsyncAgmts.jsx";

+ import { ReplAgmts } from "./replAgmts.jsx";

+ import { ReplRUV } from "./replTasks.jsx";

+ import { DoubleConfirmModal } from "../notifications.jsx";

+ import { EnableReplModal } from "./replModals.jsx";

+ import {

+     Button,

+     Col,

+     ControlLabel,

+     Icon,

+     Nav,

+     NavItem,

+     noop,

+     Row,

+     Spinner,

+     TabContainer,

+     TabContent,

+     TabPane,

+ } from "patternfly-react";

+ import PropTypes from "prop-types";

+ import { log_cmd, valid_dn } from "../tools.jsx";

+ import "../../css/ds.css";

+ 

+ export class ReplSuffix extends React.Component {

+     constructor (props) {

+         super(props);

+         this.state = {

+             loading: false,

+             activeKey: 1,

+             showDisableConfirm: false,

+             replicationEnabled: false,

+             errObj: {},

+             replEnabled: this.props.replicated,

+             // Enable replication settings

+             showEnableReplModal: false,

+             enableRole: "Master",

+             enableRID: "1",

+             enableBindDN: "cn=replication manager,cn=config",

+             enableBindPW: "",

+             enableBindPWConfirm: "",

+             enableBindGroupDN: "",

+             // Disable replication

+             showDisableReplModal: false,

+             disableChecked: false,

+             disableSpinning: false,

+             modalChecked: false,

+             modalSpinning: false,

+         };

+ 

+         // General bindings

+         this.handleReplChange = this.handleReplChange.bind(this);

+         this.handleEnableChange = this.handleEnableChange.bind(this);

+         this.handleChange = this.handleChange.bind(this);

+         this.handleNavSelect = this.handleNavSelect.bind(this);

+         this.disableReplication = this.disableReplication.bind(this);

+         this.enableReplication = this.enableReplication.bind(this);

+         this.closeEnableReplModal = this.closeEnableReplModal.bind(this);

+         this.closeDisableReplModal = this.closeDisableReplModal.bind(this);

+     }

+ 

+     componentDidMount() {

+         this.props.enableTree();

+     }

+ 

+     handleNavSelect(key) {

+         this.setState({ activeKey: key });

+     }

+ 

+     handleReplChange() {

+         if (this.props.replicated) {

+             // Disable Replication

+             this.setState({

+                 showDisableReplModal: true,

+                 modalChecked: false,

+                 modalSpinning: false

+             });

+         } else {

+             // Enable replication

+             this.setState({

+                 showEnableReplModal: true

+             });

+         }

+     }

+ 

+     handleChange (e) {

+         let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;

+         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

+         });

+     }

+ 

+     handleEnableChange (e) {

+         let value = e.target.value;

+         let attr = e.target.id;

+         let valueErr = false;

+         let errObj = this.state.errObj;

+ 

+         if (attr == "enableBindDN" && value != "" && !valid_dn(value)) {

+             valueErr = true;

+         }

+         if (attr == "enableBindGroupDN" && value != "" && !valid_dn(value)) {

+             valueErr = true;

+         }

+         if (attr == "enableBindPW") {

+             if (value != this.state.enableBindPWConfirm) {

+                 valueErr = true;

+             } else {

+                 errObj.enableBindPW = false;

+                 errObj.enableBindPWConfirm = false;

+             }

+         }

+         if (attr == "enableBindPWConfirm") {

+             if (value != this.state.enableBindPW) {

+                 valueErr = true;

+             } else {

+                 errObj.enableBindPW = false;

+                 errObj.enableBindPWConfirm = false;

+             }

+         }

+         errObj[attr] = valueErr;

+         this.setState({

+             [attr]: value,

+             errObj: errObj

+         });

+     }

+ 

+     closeEnableReplModal () {

+         this.setState({

+             showEnableReplModal: false,

+         });

+     }

+ 

+     enableReplication () {

+         // First, Validate

+         if (this.state.enableBindDN != "" && !valid_dn(this.state.enableBindDN)) {

+             this.props.addNotification(

+                 "error",

+                 `The Bind DN is not a valid DN (Distinguished Name) ${this.state.enableBindDN}`

+             );

+             return;

+         }

+         if (this.state.enableBindGroupDN != "" && !valid_dn(this.state.enableBindGroupDN)) {

+             this.props.addNotification(

+                 "error",

+                 `The Group DN is not a valid DN (Distinguished Name)`

+             );

+             return;

+         }

+         if (this.state.enableBindPW != this.state.enableBindPWConfirm) {

+             this.props.addNotification(

+                 "error",

+                 `The Bind DN passwords do not match`

+             );

+             return;

+         }

+         if (this.state.enableRID != "" && (this.state.enableRID < 1 || this.state.enableRID > 65534)) {

+             this.props.addNotification(

+                 "error",

+                 `The Replica ID is not in the valid range of 1 - 65534`

+             );

+             return;

+         }

+ 

+         // Now enable replication

+         let cmd = [

+             'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',

+             'replication', 'enable', '--suffix=' + this.props.suffix,

+             '--role=' + this.state.enableRole

+         ];

+         if (this.state.enableBindDN != "") {

+             cmd.push('--bind-dn=' + this.state.enableBindDN);

+         }

+         if (this.state.enableBindPW != "") {

+             cmd.push('--bind-passwd=' + this.state.enableBindPW);

+         }

+         if (this.state.enableBindGroupDN != "") {

+             cmd.push('--bind-group-dn=' + this.state.enableBindGroupDN);

+         }

+         if (this.state.enableRole == "Master") {

+             cmd.push('--replica-id=' + this.state.enableRID);

+         }

+ 

+         this.props.disableTree();

+         log_cmd('enableReplication', 'Enable replication', cmd);

+         cockpit

+                 .spawn(cmd, { superuser: true, err: "message" })

+                 .done(content => {

+                     this.props.reload(1);

+                     this.props.addNotification(

+                         "success",

+                         `Successfully enabled replication for "${this.props.suffix}"`

+                     );

+                 })

+                 .fail(err => {

+                     this.props.reload(1);

+                     let errMsg = JSON.parse(err);

+                     this.props.addNotification(

+                         "error",

+                         `Failed to enable replication for "${this.props.suffix}" - ${errMsg.desc}`

+                     );

+                 });

+     }

+ 

+     closeDisableReplModal () {

+         this.setState({

+             showDisableReplModal: false

+         });

+     }

+ 

+     disableReplication () {

+         this.props.disableTree();

+         let cmd = ['dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket', 'replication', 'disable', '--suffix=' + this.props.suffix];

+         log_cmd('disableReplication', 'Disable replication', cmd);

+         cockpit

+                 .spawn(cmd, { superuser: true, err: "message" })

+                 .done(content => {

+                     this.props.reload(1);

+                     this.props.addNotification(

+                         "success",

+                         `Successfully disabled replication for "${this.props.suffix}"`

+                     );

+                 })

+                 .fail(err => {

+                     this.props.reload(1);

+                     let errMsg = JSON.parse(err);

+                     this.props.addNotification(

+                         "error",

+                         `Failed to disable replication for "${this.props.suffix}" - ${errMsg.desc}`

+                     );

+                 });

+     }

+ 

+     //

+     // Render the component

+     //

+     render () {

+         let spinning = "";

+         let spintext = "";

+         let suffixIcon = "tree";

+         if (this.props.replicated) {

+             suffixIcon = "clone";

+         } else {

+             if (this.props.repl == "subsuffix") {

+                 suffixIcon = "leaf";

+             }

+         }

+         if (this.props.spinning) {

+             spinning =

+                 <Spinner className="ds-margin-top ds-margin-left ds-inline-spinner" loading inline size="sm" />;

+             spintext =

+                 <font size="2"><i>Refreshing</i></font>;

+         }

+         let suffixClass = "ds-margin-top-xlg";

+         if (this.props.disabled) {

+             suffixClass = "ds-margin-top-xlg ds-disabled";

+         }

+         let replAgmtNavTitle = 'Replication Agreements <font size="1">(' + this.props.agmtRows.length + ')</font>';

+         let winsyncNavTitle = 'Winsync Agreements <font size="1">(' + this.props.winsyncRows.length + ')</font>';

+ 

+         let enabledContent =

+             <div className={suffixClass}>

+                 <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: 'Configuration'}} />

+                             </NavItem>

+                             <NavItem eventKey={2}>

+                                 <div dangerouslySetInnerHTML={{__html: replAgmtNavTitle}} />

+                             </NavItem>

+                             <NavItem eventKey={3}>

+                                 <div dangerouslySetInnerHTML={{__html: winsyncNavTitle}} />

+                             </NavItem>

+                             <NavItem eventKey={4}>

+                                 <div dangerouslySetInnerHTML={{__html: "RUV's & Tasks"}} />

+                             </NavItem>

+                         </Nav>

+                         <TabContent>

+                             <TabPane eventKey={1}>

+                                 <ReplConfig

+                                     suffix={this.props.suffix}

+                                     role={this.props.role}

+                                     data={this.props.data}

+                                     serverId={this.props.serverId}

+                                     addNotification={this.props.addNotification}

+                                     reload={this.props.reload}

+                                     reloadConfig={this.props.reloadConfig}

+                                 />

+                             </TabPane>

+                             <TabPane eventKey={2}>

+                                 <ReplAgmts

+                                     suffix={this.props.suffix}

+                                     serverId={this.props.serverId}

+                                     rows={this.props.agmtRows}

+                                     addNotification={this.props.addNotification}

+                                     reload={this.props.reloadAgmts}

+                                     attrs={this.props.attrs}

+                                     disableTable={this.props.disableAgmtTable}

+                                     key={this.props.agmtRows}

+                                 />

+                             </TabPane>

+                             <TabPane eventKey={3}>

+                                 <WinsyncAgmts

+                                     suffix={this.props.suffix}

+                                     serverId={this.props.serverId}

+                                     rows={this.props.winsyncRows}

+                                     addNotification={this.props.addNotification}

+                                     reload={this.props.reloadWinsyncAgmts}

+                                     attrs={this.props.attrs}

+                                     disableTable={this.props.disableWSAgmtTable}

+                                     key={this.props.winsyncRows}

+                                 />

+                             </TabPane>

+                             <TabPane eventKey={4}>

+                                 <ReplRUV

+                                     suffix={this.props.suffix}

+                                     serverId={this.props.serverId}

+                                     rows={this.props.ruvRows}

+                                     addNotification={this.props.addNotification}

+                                     reload={this.props.reloadRUV}

+                                     localRID={this.props.data.nsds5replicaid}

+                                     key={this.props.ruvRows}

+                                 />

+                             </TabPane>

+                         </TabContent>

+                     </div>

+                 </TabContainer>

+             </div>;

+ 

+         let replActionButton = "";

+         if (this.props.replicated) {

+             replActionButton =

+                 <Button

+                     bsStyle="danger"

+                     onClick={this.handleReplChange}

+                     title="Disable replication, and remove all replication agreements."

+                 >

+                     Disable

+                 </Button>;

+         } else {

+             enabledContent =

+                 <div className="ds-center ds-margin-top-xlg">

+                     <h4>

+                         Replication is not enabled for this suffix

+                     </h4>

+                     <Button

+                         bsStyle="primary"

+                         onClick={this.handleReplChange}

+                         className="ds-margin-top-lg"

+                     >

+                         Enable Replication

+                     </Button>

+                 </div>;

+         }

+ 

+         return (

+             <div id="suffix-page">

+                 <Row>

+                     <Col sm={8} className="ds-word-wrap">

+                         <ControlLabel className="ds-suffix-header"><Icon type="fa" name={suffixIcon} />

+                             {" " + this.props.suffix}

+                             <Icon className="ds-left-margin ds-refresh"

+                                 type="fa" name="refresh" title="Refresh replication settings for this suffix"

+                                 onClick={() => {

+                                     this.props.reload(false);

+                                 }}

+                             />

+                             {spinning} {spintext}

+                         </ControlLabel>

+                     </Col>

+                     <Col sm={4}>

+                         <Row>

+                             <Col className="ds-no-padding ds-container" componentClass={ControlLabel} sm={12}>

+                                 {replActionButton}

+                             </Col>

+                         </Row>

+                     </Col>

+                 </Row>

+                 <p />

+                 {enabledContent}

+                 <EnableReplModal

+                     showModal={this.state.showEnableReplModal}

+                     closeHandler={this.closeEnableReplModal}

+                     handleChange={this.handleEnableChange}

+                     saveHandler={this.enableReplication}

+                     spinning={this.state.addManagerSpinning}

+                     role={this.state.enableRole}

+                     error={this.state.errObj}

+                 />

+                 <DoubleConfirmModal

+                     showModal={this.state.showDisableReplModal}

+                     closeHandler={this.closeDisableReplModal}

+                     handleChange={this.handleChange}

+                     actionHandler={this.disableReplication}

+                     spinning={this.state.modalSpinning}

+                     item={this.props.suffix}

+                     checked={this.state.modalChecked}

+                     mTitle="Disable Replication"

+                     mMsg="Are you sure you want to disable replication for this suffix?"

+                     mSpinningMsg="Disabling Replication ..."

+                     mBtnName="Disable Replication"

+                 />

+             </div>

+         );

+     }

+ }

+ 

+ ReplSuffix.propTypes = {

+     serverId: PropTypes.string,

+     suffix: PropTypes.string,

+     role: PropTypes.string,

+     addNotification: PropTypes.func,

+     agmtRows: PropTypes.array,

+     winsyncRows: PropTypes.array,

+     ruvRows: PropTypes.array,

+     reloadAgmts: PropTypes.func,

+     reloadRUV: PropTypes.func,

+     reloadConfig: PropTypes.func,

+     reload: PropTypes.func,

+     replicated: PropTypes.bool,

+     attrs: PropTypes.array,

+     enableTree: PropTypes.func,

+     disableTree: PropTypes.func,

+     spinning: PropTypes.bool,

+     disabled: PropTypes.bool,

+ };

+ 

+ ReplSuffix.defaultProps = {

+     serverId: "",

+     suffix: "",

+     role: "",

+     addNotification: noop,

+     agmtRows: [],

+     winsyncRows: [],

+     ruvRows: [],

+     reloadAgmts: noop,

+     reloadRUV: noop,

+     reloadConfig: noop,

+     reload: noop,

+     replicated: false,

+     attrs: [],

+     enableTree: noop,

+     disableTree: noop,

+     spinning: false,

+     disabled: false,

+ };

@@ -0,0 +1,608 @@ 

+ import React from "react";

+ import {

+     Button,

+     DropdownButton,

+     MenuItem,

+     actionHeaderCellFormatter,

+     sortableHeaderCellFormatter,

+     tableCellFormatter,

+     noop

+ } from "patternfly-react";

+ import { DSTable, DSShortTable } from "../dsTable.jsx";

+ import PropTypes from "prop-types";

+ import "../../css/ds.css";

+ 

+ class ReplAgmtTable extends React.Component {

+     constructor(props) {

+         super(props);

+         this.state = {

+             searchField: "Agreements",

+             fieldsToSearch: ["name"],

+             rowKey: "name",

+             columns: [

+                 {

+                     property: "name",

+                     header: {

+                         label: "Agreement Name",

+                         props: {

+                             index: 0,

+                             rowSpan: 1,

+                             colSpan: 1,

+                             sort: true

+                         },

+                         transforms: [],

+                         formatters: [],

+                         customFormatters: [sortableHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 0

+                         },

+                         formatters: [tableCellFormatter]

+                     }

+                 },

+                 {

+                     property: "host",

+                     header: {

+                         label: "Replica Hostname",

+                         props: {

+                             index: 1,

+                             rowSpan: 1,

+                             colSpan: 1,

+                             sort: true

+                         },

+                         transforms: [],

+                         formatters: [],

+                         customFormatters: [sortableHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 1

+                         },

+                         formatters: [tableCellFormatter]

+                     }

+                 },

+                 {

+                     property: "port",

+                     header: {

+                         label: "Replica Port",

+                         props: {

+                             index: 2,

+                             rowSpan: 1,

+                             colSpan: 1,

+                             sort: true

+                         },

+                         transforms: [],

+                         formatters: [],

+                         customFormatters: [sortableHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 2

+                         },

+                         formatters: [tableCellFormatter]

+                     }

+                 },

+                 {

+                     property: "state",

+                     header: {

+                         label: "State",

+                         props: {

+                             index: 3,

+                             rowSpan: 1,

+                             colSpan: 1,

+                             sort: true

+                         },

+                         transforms: [],

+                         formatters: [],

+                         customFormatters: [sortableHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 3

+                         },

+                         formatters: [tableCellFormatter]

+                     }

+                 },

+                 {

+                     property: "status",

+                     header: {

+                         label: "Last Update Status",

+                         props: {

+                             index: 4,

+                             rowSpan: 1,

+                             colSpan: 1,

+                             sort: true

+                         },

+                         transforms: [],

+                         formatters: [],

+                         customFormatters: [sortableHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 4

+                         },

+                         formatters: [tableCellFormatter]

+                     }

+                 },

+                 {

+                     property: "initstatus",

+                     header: {

+                         label: "Last Init Status",

+                         props: {

+                             index: 5,

+                             rowSpan: 1,

+                             colSpan: 1,

+                             sort: true

+                         },

+                         transforms: [],

+                         formatters: [],

+                         customFormatters: [sortableHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 5

+                         },

+                     }

+                 },

+                 {

+                     property: "actions",

+                     header: {

+                         props: {

+                             index: 6,

+                             rowSpan: 1,

+                             colSpan: 1

+                         },

+                         formatters: [actionHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 6

+                         },

+                         formatters: [

+                             (value, { rowData }) => {

+                                 return [

+                                     <td key={rowData.name[0]}>

+                                         <DropdownButton

+                                             pullRight

+                                             id={rowData.name[0]}

+                                             className="ds-action-button"

+                                             bsStyle="primary" title="Actions"

+                                         >

+                                             <MenuItem eventKey="1" onClick={() => {

+                                                 this.props.edit(rowData.name[0]);

+                                             }}

+                                             >

+                                                 Edit Agreement

+                                             </MenuItem>

+                                             <MenuItem eventKey="2" onClick={() => {

+                                                 this.props.init(rowData.name[0]);

+                                             }}

+                                             >

+                                                 Initialize Agreement

+                                             </MenuItem>

+                                             <MenuItem eventKey="3" onClick={() => {

+                                                 this.props.poke(rowData.name[0]);

+                                             }}

+                                                 title="Awaken agreement if it is sleeping"

+                                             >

+                                                 Poke Agreement

+                                             </MenuItem>

+                                             <MenuItem eventKey="4" onClick={() => {

+                                                 this.props.enable(rowData.name[0], rowData.state[0]);

+                                             }}

+                                             >

+                                                 Disable/Enable Agreement

+                                             </MenuItem>

+                                             <MenuItem divider />

+                                             <MenuItem eventKey="3" onClick={() => {

+                                                 this.props.delete(rowData.name[0], rowData.state[0]);

+                                             }}

+                                             >

+                                                 Delete Agreement

+                                             </MenuItem>

+                                         </DropdownButton>

+                                     </td>

+                                 ];

+                             }

+                         ]

+                     }

+                 },

+             ],

+         };

+ 

+         this.getColumns = this.getColumns.bind(this);

+         this.getSingleColumn = this.getSingleColumn.bind(this);

+     }

+ 

+     getSingleColumn () {

+         return [

+             {

+                 property: "msg",

+                 header: {

+                     label: "Agreements",

+                     props: {

+                         index: 0,

+                         rowSpan: 1,

+                         colSpan: 1,

+                         sort: true

+                     },

+                     transforms: [],

+                     formatters: [],

+                     customFormatters: [sortableHeaderCellFormatter]

+                 },

+                 cell: {

+                     props: {

+                         index: 0

+                     },

+                     formatters: [tableCellFormatter]

+                 }

+             },

+         ];

+     }

+ 

+     getColumns() {

+         return this.state.columns;

+     }

+ 

+     render() {

+         let agmtTable;

+         if (this.props.rows.length == 0) {

+             agmtTable =

+                 <DSShortTable

+                     getColumns={this.getSingleColumn}

+                     rowKey={"msg"}

+                     rows={[{msg: "No Agreements"}]}

+                 />;

+         } else {

+             agmtTable =

+                 <DSTable

+                     getColumns={this.getColumns}

+                     fieldsToSearch={this.state.fieldsToSearch}

+                     toolBarSearchField={this.state.searchField}

+                     rowKey={this.state.rowKey}

+                     rows={this.props.rows}

+                     disableLoadingSpinner

+                     toolBarPagination={[6, 12, 24, 48, 96]}

+                     toolBarPaginationPerPage={6}

+                 />;

+         }

+ 

+         return (

+             <div className="ds-margin-top-xlg">

+                 {agmtTable}

+             </div>

+ 

+         );

+     }

+ }

+ 

+ class ManagerTable extends React.Component {

+     constructor(props) {

+         super(props);

+ 

+         this.state = {

+             rowKey: "name",

+             columns: [

+                 {

+                     property: "name",

+                     header: {

+                         label: "Replication Manager",

+                         props: {

+                             index: 0,

+                             rowSpan: 1,

+                             colSpan: 1,

+                             sort: true

+                         },

+                         transforms: [],

+                         formatters: [],

+                         customFormatters: [sortableHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 0

+                         },

+                         formatters: [tableCellFormatter]

+                     }

+                 },

+                 {

+                     property: "actions",

+                     header: {

+                         label: "",

+                         props: {

+                             index: 1,

+                             rowSpan: 1,

+                             colSpan: 1

+                         },

+                         formatters: [actionHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 1

+                         },

+                         formatters: [

+                             (value, { rowData }) => {

+                                 return [

+                                     <td key={rowData.name[0]}>

+                                         <Button

+                                             bsStyle="default"

+                                             onClick={() => {

+                                                 this.props.confirmDelete(rowData);

+                                             }}

+                                             title="Delete replication manager"

+                                         >

+                                             Delete

+                                         </Button>

+                                     </td>

+                                 ];

+                             }

+                         ]

+                     }

+                 }

+             ]

+         };

+         this.getColumns = this.getColumns.bind(this);

+         this.getSingleColumn = this.getSingleColumn.bind(this);

+     }

+ 

+     getSingleColumn () {

+         return [

+             {

+                 property: "msg",

+                 header: {

+                     label: "Replication Managers",

+                     props: {

+                         index: 0,

+                         rowSpan: 1,

+                         colSpan: 1,

+                         sort: true

+                     },

+                     transforms: [],

+                     formatters: [],

+                     customFormatters: [sortableHeaderCellFormatter]

+                 },

+                 cell: {

+                     props: {

+                         index: 0

+                     },

+                     formatters: [tableCellFormatter]

+                 }

+             },

+         ];

+     }

+ 

+     getColumns() {

+         return this.state.columns;

+     }

+ 

+     render() {

+         let managerTable;

+         if (this.props.rows.length == 0) {

+             managerTable = <DSShortTable

+                 getColumns={this.getSingleColumn}

+                 rowKey={"msg"}

+                 rows={[{msg: "No Replication Managers"}]}

+             />;

+         } else {

+             managerTable = <DSShortTable

+                 getColumns={this.getColumns}

+                 rowKey={this.state.rowKey}

+                 rows={this.props.rows}

+                 disableLoadingSpinner

+             />;

+         }

+         return (

+             <div>

+                 {managerTable}

+             </div>

+         );

+     }

+ }

+ 

+ class RUVTable extends React.Component {

+     constructor(props) {

+         super(props);

+ 

+         this.state = {

+             rowKey: "rid",

+             columns: [

+                 {

+                     property: "rid",

+                     header: {

+                         label: "Replica ID",

+                         props: {

+                             index: 0,

+                             rowSpan: 1,

+                             colSpan: 1,

+                             sort: true

+                         },

+                         transforms: [],

+                         formatters: [],

+                         customFormatters: [sortableHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 0

+                         },

+                         formatters: [tableCellFormatter]

+                     }

+                 },

+                 {

+                     property: "url",

+                     header: {

+                         label: "Replica LDAP URL",

+                         props: {

+                             index: 1,

+                             rowSpan: 1,

+                             colSpan: 1,

+                             sort: true

+                         },

+                         transforms: [],

+                         formatters: [],

+                         customFormatters: [sortableHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 1

+                         },

+                         formatters: [tableCellFormatter]

+                     }

+                 },

+                 {

+                     property: "maxcsn",

+                     header: {

+                         label: "Max CSN",

+                         props: {

+                             index: 2,

+                             rowSpan: 1,

+                             colSpan: 1,

+                             sort: true

+                         },

+                         transforms: [],

+                         formatters: [],

+                         customFormatters: [sortableHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 2

+                         },

+                         formatters: [tableCellFormatter]

+                     }

+                 },

+                 {

+                     property: "actions",

+                     header: {

+                         label: "",

+                         props: {

+                             index: 3,

+                             rowSpan: 1,

+                             colSpan: 1

+                         },

+                         formatters: [actionHeaderCellFormatter]

+                     },

+                     cell: {

+                         props: {

+                             index: 3

+                         },

+                         formatters: [

+                             (value, { rowData }) => {

+                                 return [

+                                     <td key={rowData.rid[0]}>

+                                         <Button

+                                             bsStyle="default"

+                                             onClick={() => {

+                                                 this.props.confirmDelete(rowData.rid[0]);

+                                             }}

+                                             title="Attempt to clean and remove this Replica ID from this suffix"

+                                         >

+                                             Clean

+                                         </Button>

+                                     </td>

+                                 ];

+                             }

+                         ]

+                     }

+                 }

+             ]

+         };

+         this.getColumns = this.getColumns.bind(this);

+         this.getSingleColumn = this.getSingleColumn.bind(this);

+     }

+ 

+     getSingleColumn () {

+         return [

+             {

+                 property: "msg",

+                 header: {

+                     label: "Remote RUV's",

+                     props: {

+                         index: 0,

+                         rowSpan: 1,

+                         colSpan: 1,

+                         sort: true

+                     },

+                     transforms: [],

+                     formatters: [],

+                     customFormatters: [sortableHeaderCellFormatter]

+                 },

+                 cell: {

+                     props: {

+                         index: 0

+                     },

+                     formatters: [tableCellFormatter]

+                 }

+             },

+         ];

+     }

+ 

+     getColumns() {

+         return this.state.columns;

+     }

+ 

+     render() {

+         let ruvTable;

+         if (this.props.rows.length == 0) {

+             ruvTable = <DSShortTable

+                 getColumns={this.getSingleColumn}

+                 rowKey={"msg"}

+                 rows={[{msg: "No RUV's"}]}

+             />;

+         } else {

+             ruvTable = <DSShortTable

+                 getColumns={this.getColumns}

+                 rowKey={this.state.rowKey}

+                 rows={this.props.rows}

+                 disableLoadingSpinner

+             />;

+         }

+         return (

+             <div>

+                 {ruvTable}

+             </div>

+         );

+     }

+ }

+ 

+ ReplAgmtTable.propTypes = {

+     rows: PropTypes.array,

+     edit: PropTypes.func,

+     poke: PropTypes.func,

+     init: PropTypes.func,

+     enable: PropTypes.func,

+     delete: PropTypes.func,

+ };

+ 

+ ReplAgmtTable.defaultProps = {

+     rows: [],

+     edit: noop,

+     poke: noop,

+     init: noop,

+     enable: noop,

+     delete: noop,

+ };

+ 

+ ManagerTable.propTypes = {

+     rows: PropTypes.array,

+     confirmDelete: PropTypes.func

+ };

+ 

+ ManagerTable.defaultProps = {

+     rows: [],

+     confirmDelete: noop

+ };

+ 

+ RUVTable.propTypes = {

+     rows: PropTypes.array,

+     confirmDelete: PropTypes.func

+ };

+ 

+ RUVTable.defaultProps = {

+     rows: [],

+     confirmDelete: noop

+ };

+ 

+ export {

+     ReplAgmtTable,

+     ManagerTable,

+     RUVTable,

+ };

@@ -0,0 +1,314 @@ 

+ import cockpit from "cockpit";

+ import React from "react";

+ import { log_cmd, bad_file_name } from "../tools.jsx";

+ import { RUVTable } from "./replTables.jsx";

+ import { ExportModal } from "./replModals.jsx";

+ import { DoubleConfirmModal } from "../notifications.jsx";

+ import PropTypes from "prop-types";

+ import {

+     Button,

+     Col,

+     ControlLabel,

+     Icon,

+     noop,

+     Row,

+ } from "patternfly-react";

+ 

+ export class ReplRUV extends React.Component {

+     constructor(props) {

+         super(props);

+ 

+         this.state = {

+             errObj: {},

+             rid: "",

+             localRID: "",

+             ldifLocatio: "",

+             saveOK: false,

+             showConfirmCleanRUV: false,

+             modalChecked: false,

+             modalSpinning: false,

+         };

+         this.showConfirmCleanRUV = this.showConfirmCleanRUV.bind(this);

+         this.closeConfirmCleanRUV = this.closeConfirmCleanRUV.bind(this);

+         this.showConfirmExport = this.showConfirmExport.bind(this);

+         this.closeConfirmExport = this.closeConfirmExport.bind(this);

+         this.handleLDIFChange = this.handleLDIFChange.bind(this);

+         this.handleChange = this.handleChange.bind(this);

+         this.cleanRUV = this.cleanRUV.bind(this);

+         this.doExport = this.doExport.bind(this);

+     }

+ 

+     showConfirmCleanRUV (rid) {

+         this.setState({

+             rid: rid,

+             showConfirmCleanRUV: true,

+             modalChecked: false,

+             modalSpinning: false,

+         });

+     }

+ 

+     closeConfirmCleanRUV () {

+         this.setState({

+             showConfirmCleanRUV: false,

+             modalChecked: false,

+             modalSpinning: false,

+         });

+     }

+ 

+     showConfirmExport () {

+         this.setState({

+             saveOK: false,

+             showConfirmExport: true,

+         });

+     }

+ 

+     closeConfirmExport () {

+         this.setState({

+             showConfirmExport: false,

+         });

+     }

+ 

+     cleanRUV () {

+         // Enable/disable agmt

+         let cmd = ['dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',

+             'repl-tasks', 'cleanallruv', '--replica-id=' + this.state.rid,

+             '--force-cleaning', '--suffix=' + this.props.suffix];

+ 

+         log_cmd('cleanRUV', 'Clean the rid', cmd);

+         cockpit

+                 .spawn(cmd, {superuser: true, "err": "message"})

+                 .done(content => {

+                     this.props.reload(this.props.suffix);

+                     this.props.addNotification(

+                         'success',

+                         'Successfully started CleanAllRUV task');

+                     this.closeConfirmCleanRUV();

+                 })

+                 .fail(err => {

+                     let errMsg = JSON.parse(err);

+                     this.props.addNotification(

+                         "error",

+                         `Failed to start CleanAllRUV task - ${errMsg.desc}`

+                     );

+                     this.closeConfirmCleanRUV();

+                 });

+     }

+ 

+     handleLDIFChange (e) {

+         let value = e.target.value;

+         let saveOK = true;

+         if (value == "" || bad_file_name(value)) {

+             saveOK = false;

+         }

+         this.setState({

+             [e.target.id]: value,

+             saveOK: saveOK

+         });

+     }

+ 

+     handleChange (e) {

+         this.setState({

+             [e.target.id]: e.target.checked,

+         });

+     }

+ 

+     doExport () {

+         // Do Export

+         let export_cmd = [

+             "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",

+             "backend", "export", this.props.suffix, "--ldif=" + this.state.ldifLocation,

+             '--replication', "--encrypted"

+         ];

+ 

+         this.setState({

+             exportSpinner: true,

+         });

+ 

+         log_cmd("doExport", "replication do online export", export_cmd);

+         cockpit

+                 .spawn(export_cmd, { superuser: true, err: "message" })

+                 .done(content => {

+                     this.props.addNotification(

+                         "success",

+                         `Database export complete`

+                     );

+                     this.setState({

+                         showConfirmExport: false,

+                         exportSpinner: false,

+                     });

+                 })

+                 .fail(err => {

+                     let errMsg = JSON.parse(err);

+                     this.props.addNotification(

+                         "error",

+                         `Error exporting database - ${errMsg.desc}`

+                     );

+                     this.setState({

+                         showConfirmExport: false,

+                         exportSpinner: false,

+                     });

+                 });

+     }

+ 

+     render() {

+         // Strip outthe loca RUV and display it diffent then onmly allow cleaning of remote rids

+         let remote_rows = [];

+         let localRID = "";

+         let localURL = "";

+         let localCSN = "";

+         let localRawCSN = "";

+         let localMinCSN = "";

+         let localRawMinCSN = "";

+         for (let row of this.props.rows) {

+             if (row.rid == this.props.localRID) {

+                 localRID = row.rid;

+                 localURL = row.url;

+                 localCSN = row.maxcsn;

+                 localRawCSN = row.raw_maxcsn;

+                 localMinCSN = row.csn;

+                 localRawMinCSN = row.raw_csn;

+             } else {

+                 remote_rows.push(row);

+             }

+         }

+         let localRUV =

+             <div className="ds-left-indent-md">

+                 <Row className="ds-margin-top-med">

+                     <Col sm={2}>

+                         <ControlLabel>

+                             Replica ID

+                         </ControlLabel>

+                     </Col>

+                     <Col sm={10}>

+                         <b>{localRID}</b>

+                     </Col>

+                 </Row>

+                 <Row>

+                     <Col sm={2}>

+                         <ControlLabel>

+                             LDAP URL

+                         </ControlLabel>

+                     </Col>

+                     <Col sm={10}>

+                         <b>{localURL}</b>

+                     </Col>

+                 </Row>

+                 <Row>

+                     <Col sm={2}>

+                         <ControlLabel>

+                             Min CSN

+                         </ControlLabel>

+                     </Col>

+                     <Col sm={10}>

+                         <b>{localMinCSN}</b> ({localRawMinCSN})

+                     </Col>

+                 </Row>

+                 <Row>

+                     <Col sm={2}>

+                         <ControlLabel>

+                             Max CSN

+                         </ControlLabel>

+                     </Col>

+                     <Col sm={10}>

+                         <b>{localCSN}</b> ({localRawCSN})

+                     </Col>

+                 </Row>

+             </div>;

+ 

+         if (localRID == "") {

+             localRUV =

+                 <div className="ds-indent">

+                     <i>

+                         There is no local RUV, the database might not have been initialized yet.

+                     </i>

+                 </div>;

+         }

+ 

+         return (

+             <div className="ds-margin-top-xlg ds-indent">

+                 <ControlLabel className="ds-h4">

+                     Local RUV

+                     <Icon className="ds-left-margin ds-refresh"

+                         type="fa" name="refresh" title="Refresh the RUV for this suffix"

+                         onClick={() => {

+                             this.props.reload(this.props.suffix);

+                         }}

+                     />

+                 </ControlLabel>

+                 {localRUV}

+                 <hr />

+                 <Row className="ds-margin-top">

+                     <Col sm={12} className="ds-word-wrap">

+                         <ControlLabel className="ds-h4">

+                             Remote RUV's

+                             <Icon className="ds-left-margin ds-refresh"

+                                 type="fa" name="refresh" title="Refresh the RUV for this suffix"

+                                 onClick={() => {

+                                     this.props.reload(this.props.suffix);

+                                 }}

+                             />

+                         </ControlLabel>

+                     </Col>

+                 </Row>

+                 <div className="ds-left-indent-md ds-margin-top-lg">

+                     <RUVTable

+                         rows={remote_rows}

+                         confirmDelete={this.showConfirmCleanRUV}

+                     />

+                 </div>

+                 <hr />

+                 <h4>Create Initialization LDIF File</h4>

+                 <div className="ds-left-indent-md ds-margin-top-lg">

+                     <p><i>Export this suffix with the replication metadata needed to initialize another replica</i></p>

+                     <Button

+                         bsStyle="primary"

+                         onClick={this.showConfirmExport}

+                         className="ds-margin-top"

+                         title="See Database Tab -> Backups & LDIFs to manage the new LDIF"

+                     >

+                         Export To LDIF

+                     </Button>

+                 </div>

+                 <DoubleConfirmModal

+                     showModal={this.state.showConfirmCleanRUV}

+                     closeHandler={this.closeConfirmCleanRUV}

+                     handleChange={this.handleChange}

+                     actionHandler={this.cleanRUV}

+                     spinning={this.state.modalSpinning}

+                     item={"Replica ID " + this.state.rid}

+                     checked={this.state.modalChecked}

+                     mTitle="Remove RUV Element (CleanAllRUV)"

+                     mMsg="Are you sure you want to attempt to clean this Replica ID from the suffix?"

+                     mSpinningMsg="Starting cleaning task (CleanAllRUV) ..."

+                     mBtnName="Remove RUV Element"

+                 />

+                 <ExportModal

+                     showModal={this.state.showConfirmExport}

+                     closeHandler={this.closeConfirmExport}

+                     handleChange={this.handleLDIFChange}

+                     saveHandler={this.doExport}

+                     spinning={this.state.exportSpinner}

+                     saveOK={this.state.saveOK}

+                 />

+             </div>

+         );

+     }

+ }

+ 

+ ReplRUV.propTypes = {

+     suffix: PropTypes.string,

+     serverId: PropTypes.string,

+     rows: PropTypes.array,

+     addNotification: PropTypes.func,

+     localRID: PropTypes.string,

+     reload: PropTypes.func,

+ };

+ 

+ ReplRUV.defaultProps = {

+     serverId: "",

+     suffix: "",

+     rows: [],

+     addNotification: noop,

+     localRID: "",

+     reload: noop,

+ };

@@ -10,7 +10,7 @@ 

      Spinner,

      noop

  } from "patternfly-react";

- import { ConfirmPopup } from "../../lib/notifications.jsx";

+ import { DoubleConfirmModal } from "../../lib/notifications.jsx";

  import {

      CertTable

  } from "./securityTables.jsx";
@@ -41,11 +41,13 @@ 

              isCACert: false,

              showConfirmCAChange: false,

              loading: false,

+             modalSpinning: false,

+             modalChecked: false,

          };

  

          this.handleNavSelect = this.handleNavSelect.bind(this);

          this.addCACert = this.addCACert.bind(this);

-         this.handleAddChange = this.handleAddChange.bind(this);

+         this.handleChange = this.handleChange.bind(this);

          this.addCert = this.addCert.bind(this);

          this.showAddModal = this.showAddModal.bind(this);

          this.closeAddModal = this.closeAddModal.bind(this);
@@ -217,6 +219,8 @@ 

          this.setState({

              showConfirmDelete: true,

              certName: dataRow.nickname[0],

+             modalSpinning: false,

+             modalChecked: false,

          });

      }

  
@@ -295,7 +299,9 @@ 

              if (!SSLFlags[0].includes('C') || !SSLFlags[0].includes('T')) {

                  // This could remove the CA cert properties, better warn user

                  this.setState({

-                     showConfirmCAChange: true

+                     showConfirmCAChange: true,

+                     modalSpinning: false,

+                     modalChecked: false

                  });

                  return;

              }
@@ -305,7 +311,9 @@ 

  

      closeConfirmCAChange () {

          this.setState({

-             showConfirmCAChange: false

+             showConfirmCAChange: false,

+             modalSpinning: false,

+             modalChecked: false,

          });

      }

  
@@ -354,8 +362,8 @@ 

                  });

      }

  

-     handleAddChange (e) {

-         const value = e.target.value;

+     handleChange (e) {

+         const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;

          let valueErr = false;

          let errObj = this.state.errObj;

  
@@ -418,6 +426,8 @@ 

      closeConfirmDelete () {

          this.setState({

              showConfirmDelete: false,

+             modalSpinning: false,

+             modalChecked: false,

          });

      }

  
@@ -567,7 +577,7 @@ 

                  <SecurityAddCertModal

                      showModal={this.state.showAddModal}

                      closeHandler={this.closeAddModal}

-                     handleChange={this.handleAddChange}

+                     handleChange={this.handleChange}

                      saveHandler={this.addCert}

                      spinning={this.state.modalSpinner}

                      error={this.state.errObj}
@@ -575,23 +585,36 @@ 

                  <SecurityAddCACertModal

                      showModal={this.state.showAddCAModal}

                      closeHandler={this.closeAddCAModal}

-                     handleChange={this.handleAddChange}

+                     handleChange={this.handleChange}

                      saveHandler={this.addCACert}

                      spinning={this.state.modalSpinner}

                      error={this.state.errObj}

                  />

-                 <ConfirmPopup

+                 <DoubleConfirmModal

                      showModal={this.state.showConfirmDelete}

                      closeHandler={this.closeConfirmDelete}

-                     actionFunc={this.delCert}

-                     msg="Are you sure you want to delete this certificate?"

-                     msgContent={this.state.certName}

+                     handleChange={this.handleChange}

+                     actionHandler={this.delCert}

+                     spinning={this.state.modalSpinning}

+                     item={this.state.certName}

+                     checked={this.state.modalChecked}

+                     mTitle="Delete Certificate"

+                     mMsg="Are you sure you want to delete this certificate?"

+                     mSpinningMsg="Deleting Certificate ..."

+                     mBtnName="Delete Certificate"

                  />

-                 <ConfirmPopup

+                 <DoubleConfirmModal

                      showModal={this.state.showConfirmCAChange}

                      closeHandler={this.closeConfirmCAChange}

-                     actionFunc={this.doEditCert}

-                     msg="Removing the 'C' or 'T' flags from the SSL trust catagory could break all TLS connectivity to and from the server, are you sure you want to proceed?"

+                     handleChange={this.handleChange}

+                     actionHandler={this.doEditCert}

+                     spinning={this.state.modalSpinning}

+                     item={this.state.certName}

+                     checked={this.state.modalChecked}

+                     mTitle="Warning - "

+                     mMsg="Removing the 'C' or 'T' flags from the SSL trust catagory could break all TLS connectivity to and from the server, are you sure you want to proceed?"

+                     mSpinningMsg="Editing CA Certificate ..."

+                     mBtnName="Change Trust Flags"

                  />

              </div>

          );

@@ -65,7 +65,7 @@ 

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

+     let date = new Date(parseInt(year), (parseInt(month) - 1), parseInt(day),

                          parseInt(hour), parseInt(minute), parseInt(sec));

      return date.toLocaleString();

  }
@@ -122,3 +122,10 @@ 

      }

      return result;

  }

+ 

+ export function valid_dn (dn) {

+     // Validate value is a valid DN (sanity validation)

+     let dn_regex = new RegExp("^([A-Za-z]+=.*)");

+     let result = dn_regex.test(dn);

+     return result;

+ }

@@ -39,9 +39,9 @@ 

              snmpData: {},

              ldbmData: {},

              serverData: {},

-             showLoading: false,

              loadingMsg: "",

              notifications: [],

+             disableTree: false,

              // Suffix

              suffixLoading: false,

              serverLoading: false,
@@ -112,9 +112,11 @@ 

          this.addNotification = this.addNotification.bind(this);

          this.removeNotification = this.removeNotification.bind(this);

          this.loadSuffixTree = this.loadSuffixTree.bind(this);

+         this.enableTree = this.enableTree.bind(this);

          this.update_tree_nodes = this.update_tree_nodes.bind(this);

          this.selectNode = this.selectNode.bind(this);

          this.loadMonitorSuffix = this.loadMonitorSuffix.bind(this);

+         this.disableSuffixLoading = this.disableSuffixLoading.bind(this);

          this.loadMonitorLDBM = this.loadMonitorLDBM.bind(this);

          this.reloadLDBM = this.reloadLDBM.bind(this);

          this.loadMonitorSNMP = this.loadMonitorSNMP.bind(this);
@@ -209,6 +211,7 @@ 

                              icon: "pficon-catalog",

                              selectable: false,

                              id: "log-monitor",

+                             state: {"expanded": true},

                              nodes: [

                                  {

                                      text: "Access Log",
@@ -279,8 +282,11 @@ 

      }

  

      selectNode(selectedNode) {

+         if (selectedNode.selected) {

+             return;

+         }

          this.setState({

-             showLoading: true

+             disableTree: true, // Disable the tree to allow node to be fully loaded

          });

  

          if (selectedNode.id == "database-monitor" ||
@@ -344,12 +350,7 @@ 

              });

          } else {

              if (selectedNode.id in this.state) {

-                 // This suffix is already cached, but it might be incomplete...

-                 if (selectedNode.type == "dblink" && this.state.nsaddcount === undefined) {

-                     this.loadMonitorChaining(selectedNode.id);

-                 } else if (selectedNode.type != "dblink" && this.state.entrycachehitratio === undefined) {

-                     this.loadMonitorSuffix(selectedNode.id);

-                 }

+                 // This suffix is already cached

                  this.setState(prevState => {

                      return {

                          nodes: this.nodeSelector(prevState.nodes, selectedNode),
@@ -597,6 +598,12 @@ 

                  });

      }

  

+     disableSuffixLoading () {

+         this.setState({

+             suffixLoading: false

+         });

+     }

+ 

      loadMonitorSuffix(suffix) {

          this.setState({

              suffixLoading: true
@@ -616,8 +623,7 @@ 

                              ...this.state[suffix],

                              suffixData: config.attrs,

                          },

-                         suffixLoading: false,

-                     });

+                     }, this.disableSuffixLoading);

                  })

                  .fail(() => {

                      // Notification of failure (could only be server down)
@@ -1001,10 +1007,20 @@ 

          ), this.loadMonitorReplication);

      }

  

+     enableTree () {

+         this.setState({

+             disableTree: false

+         });

+     }

+ 

      render() {

          const { nodes } = this.state;

          let monitorPage = "";

          let monitor_element = "";

+         let disabled = "tree-view-container";

+         if (this.state.disableTree) {

+             disabled = "tree-view-container ds-disabled";

+         }

  

          if (this.state.loaded) {

              if (this.state.node_name == "database-monitor" || this.state.node_name == "") {
@@ -1012,14 +1028,15 @@ 

                      monitor_element =

                          <div className="ds-loading-spinner ds-center">

                              <p />

-                             <h4><b>Loading database monitor information ...</b></h4>

-                             <Spinner loading size="md" />

+                             <h4>Loading database monitor information ...</h4>

+                             <Spinner className="ds-margin-top-lg" loading size="md" />

                          </div>;

                  } else {

                      monitor_element =

                          <DatabaseMonitor

                              data={this.state.ldbmData}

                              reload={this.reloadLDBM}

+                             enableTree={this.enableTree}

                          />;

                  }

              } else if (this.state.node_name == "server-monitor") {
@@ -1027,8 +1044,8 @@ 

                      monitor_element =

                          <div className="ds-loading-spinner ds-center">

                              <p />

-                             <h4><b>Loading server monitor information ...</b></h4>

-                             <Spinner loading size="md" />

+                             <h4>Loading server monitor information ...</h4>

+                             <Spinner className="ds-margin-top-lg" loading size="md" />

                          </div>;

                  } else {

                      monitor_element =
@@ -1036,6 +1053,7 @@ 

                              data={this.state.serverData}

                              reload={this.reloadServer}

                              serverId={this.props.serverId}

+                             enableTree={this.enableTree}

                          />;

                  }

              } else if (this.state.node_name == "snmp-monitor") {
@@ -1043,14 +1061,15 @@ 

                      monitor_element =

                          <div className="ds-loading-spinner ds-center">

                              <p />

-                             <h4><b>Loading SNMP monitor information ...</b></h4>

-                             <Spinner loading size="md" />

+                             <h4>Loading SNMP monitor information ...</h4>

+                             <Spinner className="ds-margin-top-lg" loading size="md" />

                          </div>;

                  } else {

                      monitor_element =

                          <SNMPMonitor

                              data={this.state.snmpData}

                              reload={this.reloadSNMP}

+                             enableTree={this.enableTree}

                          />;

                  }

              } else if (this.state.node_name == "access-log-monitor") {
@@ -1063,6 +1082,7 @@ 

                          refreshing={this.state.accessRefreshing}

                          handleRefresh={this.accessRefreshCont}

                          lines={this.state.accessLines}

+                         enableTree={this.enableTree}

                      />;

              } else if (this.state.node_name == "audit-log-monitor") {

                  monitor_element =
@@ -1074,6 +1094,7 @@ 

                          refreshing={this.state.auditRefreshing}

                          handleRefresh={this.auditRefreshCont}

                          lines={this.state.auditLines}

+                         enableTree={this.enableTree}

                      />;

              } else if (this.state.node_name == "auditfail-log-monitor") {

                  monitor_element =
@@ -1085,6 +1106,7 @@ 

                          refreshing={this.state.auditfailRefreshing}

                          handleRefresh={this.auditFailRefreshCont}

                          lines={this.state.auditfailLines}

+                         enableTree={this.enableTree}

                      />;

              } else if (this.state.node_name == "error-log-monitor") {

                  monitor_element =
@@ -1097,6 +1119,7 @@ 

                          handleRefresh={this.errorRefreshCont}

                          handleSevLevel={this.handleSevChange}

                          lines={this.state.errorLines}

+                         enableTree={this.enableTree}

                      />;

              } else if (this.state.node_name == "replication-monitor") {

                  if (this.state.replLoading) {
@@ -1104,7 +1127,7 @@ 

                          <div className="ds-loading-spinner ds-center">

                              <p />

                              <h4>Loading replication monitor information ...</h4>

-                             <Spinner loading size="md" />

+                             <Spinner className="ds-margin-top-lg" loading size="md" />

                          </div>;

                  } else {

                      if (this.state.replicatedSuffixes.length < 1) {
@@ -1139,6 +1162,7 @@ 

                                          reloadAgmts={this.reloadReplAgmts}

                                          reloadWinsyncAgmts={this.reloadReplWinsyncAgmts}

                                          reloadConflicts={this.loadConflicts}

+                                         enableTree={this.enableTree}

                                          key={this.state.replSuffix}

                                      />

                                  </div>
@@ -1152,14 +1176,14 @@ 

                          <div className="ds-loading-spinner ds-center">

                              <p />

                              <h4>Loading suffix monitor information for <b>{this.state.node_text} ...</b></h4>

-                             <Spinner loading size="md" />

+                             <Spinner className="ds-margin-top-lg" loading size="md" />

                          </div>;

                  } else if (this.state.chainingLoading) {

                      monitor_element =

                          <div className="ds-loading-spinner ds-center">

                              <p />

                              <h4>Loading chaining monitor information for <b>{this.state.node_text} ...</b></h4>

-                             <Spinner loading size="md" />

+                             <Spinner className="ds-margin-top-lg" loading size="md" />

                          </div>;

                  } else {

                      if (this.state.node_type == "dblink") {
@@ -1169,6 +1193,7 @@ 

                                  bename={this.state.bename}

                                  reload={this.loadMonitorChaining}

                                  data={this.state[this.state.node_text].chainingData}

+                                 enableTree={this.enableTree}

                                  key={this.state.node_text}

                              />;

                      } else {
@@ -1179,6 +1204,7 @@ 

                                  bename={this.state.bename}

                                  reload={this.loadMonitorSuffix}

                                  data={this.state[this.state.node_text].suffixData}

+                                 enableTree={this.enableTree}

                                  key={this.state.node_text}

                              />;

                      }
@@ -1193,13 +1219,14 @@ 

                      <div className="ds-container">

                          <div>

                              <div className="ds-tree">

-                                 <div className="tree-view-container" id="db-tree"

+                                 <div className={disabled} id="monitor-tree"

                                      style={treeViewContainerStyles}>

                                      <TreeView

                                          nodes={nodes}

                                          highlightOnHover

                                          highlightOnSelect

                                          selectNode={this.selectNode}

+                                         key={this.state.node_text}

                                      />

                                  </div>

                              </div>
@@ -1214,7 +1241,7 @@ 

                  <div className="ds-loading-spinner ds-center">

                      <p />

                      <h4>Loading monitor information ...</h4>

-                     <Spinner loading size="md" />

+                     <Spinner className="ds-margin-top-lg" loading size="md" />

                  </div>;

          }

  

@@ -1,724 +0,0 @@ 

- 

- <!-- Replication Configuration Panel -->

- <div id="repl-config" class="all-pages ds-margin-left" hidden>

-   <h3 class="ds-config-header">Replication Configuration Settings</h3>

- 

-   <label class="ds-config-label-med" for="select-repl-role">Database Suffix</Label><select

-     class="btn btn-default dropdown" id="select-repl-cfg-suffix">

-   </select>

- 

-   <div id="ds-repl-enabled" class="ds-margin-top-lg" hidden>

-     <button class="btn btn-primary" data-toggle="modal" data-target="#enable-repl-form" id="enable-repl-btn">Enable Replication</button>

-   </div>

- 

-   <div id="repl-config-content">

-     <div>

-         <label class="ds-config-label-med" for="select-repl-role">Replication Role</Label><select class="btn btn-default dropdown" id="select-repl-role">

-             <option>Master</option>

-             <option>Hub</option>

-             <option>Consumer</option>

-           </select>

-     </div>

-     <div>

-       <label id="replicaid-label" for="nsds5replicaid" class="ds-config-label-med" title=

-         "The replica identifier for Master/Supplier (nsds5ReplicaId).">

-         Replica ID</label><input class="ds-input" type="text" id="nsds5replicaid" size="5"/>

-       <p></p>

-       <button class="btn btn-danger" id="disable-repl-btn">Disable Replication</button>

-     </div>

-     <hr>

-     <div class="ds-repl-mgr">

-       <div class="ds-inline ds-repl-mgr">

-         <label for="nsds5replicabinddngroup" title=

-           "The DN of a group that contains entries that are allowed to make replication updates (nsDS5ReplicaBindDnGroup).">Bind DN Group</label><input

-           type="text" id="nsds5replicabinddngroup" size="50" />

-       </div>

-       <table class="table table-bordered ds-mgr-table" id="repl-mgr-table"

-         title="Specifies entries that can perform replication updates on this server.  Also known as supplier DN, update DN, or Replication Manager">

-         <thead>

-           <tr>

-             <th>Replication Managers</th>

-             <th></th>

-           </tr>

-         </thead>

-         <tbody>

-         </tbody>

-       </table>

-       <button class="btn btn-default ds-repl-managers-button" data-toggle="modal" data-target="#add-repl-mgr-form" id="add-repl-manager">Add Replication Manager</button>

-     </div>

- 

-     <button class="accordion ds-accordion" id="repl-config-accordion" type="button">&#9658 Show Advanced Settings</button>

-     <div class="ds-accordion-panel">

-       <div class="ds-container">

-         <div class="ds-inline">

-           <div>

-             <label for="nsds5replicareleasetimeout" class="ds-config-label" title=

-               "Sets how long an agreement should keep its replication session established while other agreements are trying to update the same replica (nsds5ReplicaReleaseTimeout).">

-               Replication Release Timeout</label><input class="ds-input ds-cfg" type="text" id="nsds5replicareleasetimeout" size="15"/>

-           </div>

-           <div>

-             <label for="nsds5replicapurgedelay" class="ds-config-label" title=

-               "NEEDS WORK (nsds5ReplicaPurgeDelay).">Replication Purge Delay</label><input class="ds-input ds-cfg" type="text" id="nsds5replicapurgedelay" size="15"/>

-           </div>

-           <div>

-             <label for="nsds5replicatombstonepurgeinterval" class="ds-config-label" title=

-               "NEEDS WORK (nsds5ReplicaTombstonePurgeInterval).">Tombstone Purge Interval</label><input class="ds-input ds-cfg" type="text" id="nsds5replicatombstonepurgeinterval" size="15"/>

-           </div>

-           <div>

-             <label for="nsds5replicaprecisetombstonepurging" class="ds-config-label" title=

-               "NEEDS WORK (nsds5ReplicaPreciseTombstonePurging).">Precise Tombstone Purging</label><input type="checkbox"

-               id="nsds5replicaprecisetombstonepurging">

-           </div>

-         </div>

-         <div class="ds-divider"></div>

-         <div class="ds-inline">

-           <div>

-             <label for="nsds5replicabinddngroupcheckinterval" class="ds-config-label" title=

-             "NEEDS WORK (nsDS5ReplicaBindDnGroupCheckInterval).">Bind DN Group Check Interval</label><input class="ds-input ds-cfg" type="text" id="nsds5replicabinddngroupcheckinterval" size="15"/>

-           </div>

-           <div>

-             <label for="nsds5replicaprotocoltimeout" class="ds-config-label" title=

-               "NEEDS WORK (nsds5ReplicaProtocolTimeout).">Replication Protocol Timeout</label><input class="ds-input ds-cfg" type="text" id="nsds5replicaprotocoltimeout" size="15"/>

-           </div>

-           <div>

-             <label for="nsds5replicabackoffmin" class="ds-config-label" title=

-               "NEEDS WORK (nsds5ReplicaBackoffMin).">Replication Backoff Minimum</label><input class="ds-input ds-cfg" type="text" id="nsds5replicabackoffmin" size="15"/>

-           </div>

-           <div>

-             <label for="nsds5replicabackoffmax" class="ds-config-label" title=

-               "NEEDS WORK (nsds5ReplicaBackoffMax).">Replication Backoff Maximum</label><input class="ds-input ds-cfg" type="text" id="nsds5replicabackoffmax" size="15"/>

-           </div>

-         </div>

-       </div>

-       <hr>

-       <div>

-         <label for="nsslapd-changelogdir" class="ds-config-label" title="The location on the filesystem for the replication changelog database (nsslapd-changelogdir).">Changelog Location</label><input

-           class="ds-cl" type="text" id="nsslapd-changelogdir" size="60" />

-       </div>

-       <div class="ds-container">

-         <div name="changelog" class="ds-line">

-           <div>

-             <label for="nsslapd-changelogmaxentries" class="ds-config-label" title=

-               "Changelog trimming parameter.  Set the maximum number of changelog entries (nsslapd-changelogmaxentries).">

-               Changelog Maximum Entries</label><input class="ds-input ds-cl" type="text" id="nsslapd-changelogmaxentries" size="15"/>

-           </div>

-           <div>

-             <label for="nsslapd-changelogmaxage" class="ds-config-label" title=

-               "Changelog trimming parameter.  This set the maximum age of a changelog entry.  It is recommended to use the same value as the ReplicationPurgeDelay.  (nsslapd-changelogmaxage).">

-               Changelog Maximum Age</label><input class="ds-input ds-cl" type="text" id="nsslapd-changelogmaxage" size="15"/>

-           </div>

-           <div>

-             <label for="nsslapd-changelogcompactdb-interval" class="ds-config-label" title=

-               "The changelog compaction internal.  Set how often the changelog will compact itself, meaning remove empty/trimmed database slots (nsslapd-changelogcompactdb-interval).">

-               Changelog Compaction Interval</label><input class="ds-input ds-cl" type="text" id="nsslapd-changelogcompactdb-interval" size="15"/>

-           </div>

-           <div>

-             <label for="nsslapd-changelogtrim-interval" class="ds-config-label" title=

-               "The changelog trimming internal.  Set how often the changelog checks if there are entries that can be purged from the changelog based on the trimming parameters (nsslapd-changelogtrim-interval).">

-               Changelog Trim Interval</label><input class="ds-input ds-cl" type="text" id="nsslapd-changelogtrim-interval" size="15"/>

-           </div>

-         </div>

-       </div>

- 

-       <!-- Buttons to create and delete changelog -->

-       <div id="cl-create-div" class="ds-margin-top">

-         <button class="btn btn-default" id="create-cl-btn">Create Changelog</button>

-       </div>

-       <div id="cl-del-div" class="ds-margin-top" hidden>

-         <button class="btn btn-danger" id="delete-cl-btn">Delete Changelog</button>

-       </div>

-       <hr>

-     </div>

-   </div>

-   <div class="ds-footer">

-     <button class="btn btn-primary" id="repl-config-save">Save</button>

-   </div>

- </div>

- 

- 

- <!-- -----------------------------

- 

- Replication Agreements

- 

- -------------------------------- -->

- <div id="repl-agmts" class="all-pages ds-margin-left" hidden>

-   <h3 class="ds-config-header">Replication Agreements for <select

-     class="btn btn-default dropdown" id="select-repl-agmt-suffix">

-   </select></h3>

- 

-   <div class="ds-page-content">

-     <table id="repl-agmt-table" class="display ds-repl-table" cellspacing="0" width="100%">

-       <thead>

-         <tr class="ds-table-header">

-           <th>Agreement Name</th>

-           <th>Host</th>

-           <th>Port</th>

-           <th>State</th>

-           <th>Last Update Status</th>

-           <th>Last Init Status</th>

-           <th>Actions</th>

-         </tr>

-       </thead>

-       <tbody id="agmt-body">

-       </tbody>

-     </table>

-     <button class="btn btn-primary" type="button" data-toggle="modal" data-target="#agmt-form" id="create-agmt">Create Replication Agreement</button><div

-         class="ds-float-right"><button class="btn btn-default ds-float-right" type="button" id="refresh-agmts-btn">Refresh Agreements</button></div>

-     <p></p>

-   </div>

- </div>

- 

- <!-- -----------------------------

- 

- Winsync Agreements

- 

- -------------------------------- -->

- <div id="repl-winsync" class="all-pages ds-margin-left" hidden>

-   <h3 class="ds-config-header">Windows Synchronization Agreements for <select

-     class="btn btn-default dropdown" id="select-repl-winsync-suffix">

-   </select></h3>

-   <div class="ds-page-content">

-     <table id="repl-winsync-agmt-table" class="display ds-repl-table" cellspacing="0" width="100%">

-       <thead>

-         <tr class="ds-table-header">

-           <th>Agreement Name</th>

-           <th>Windows Host</th>

-           <th>Port</th>

-           <th>State</th>

-           <th>Last Update Status</th>

-           <th>Last Init Status</th>

-           <th>Actions</th>

-         </tr>

-       </thead>

-       <tbody id="winsync-agmt-body">

-       </tbody>

-     </table>

-     <button class="btn btn-primary" data-toggle="modal" data-target="#winsync-agmt-form"  type="button" id="winsync-create-agmt">Create Winsync Agreement</button><div

-         class="ds-float-right"><button class="btn btn-default ds-float-right" type="button" id="refresh-winsync-agmts-btn">Refresh Agreements</button></div>

-     <p></p>

-   </div>

- </div>

- 

- <!-- -----------------------------

- 

- CleanAllRUV Tasks

- 

- -------------------------------- -->

- <div id="repl-cleanallruv" class="all-pages ds-margin-left" hidden>

-   <h3 class="ds-config-header">CleanAllRUV Tasks</h3>

-   <div class="ds-page-content">

- 

-     <div>

-       <button class="btn btn-primary" type="button" data-toggle="modal" data-target="#cleanallruv-form" id="create-cleanallruv-btn">Create CleanAllRUV Task</button>

-     </div>

-     <p></p>

-     <div class="ds-margin-top-lg">

-       <p></p>

-       <table id="repl-clean-table" class="display ds-repl-table" cellspacing="0" width="100%">

-         <caption class="ds-center"><h4>Running CleanAllRUV Tasks</h4></caption>

-         <thead>

-           <tr class="ds-table-header">

-             <th>Task Name</th>

-             <th>Creation Time</th>

-             <th>Suffix</th>

-             <th>Replica ID</th>

-             <th>Last Update Status</th>

-             <th></th>

-           </tr>

-         </thead>

-         <tbody>

-         </tbody>

-       </table>

-       <button class="btn btn-default" type="button" id="refresh-cleanlist-btn">Refresh Task List</button>

-     </div>

-   </div>

- </div>

- 

- 

- 

-   <!-- --------------------------------------------------------

- 

-   Replication Agreement Form (wizard)

- 

-   ----------------------------------------------------------- -->

- 

-   <div class="modal fade" id="agmt-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="agmt-wizard-title" aria-hidden="true">

-     <div class="modal-dialog">

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

-           <h3 class="modal-title" id="agmt-wizard-title">Create Replication Agreement</h3>

-         </div>

-         <div class="modal-body">

-           <form class="form-horizontal">

-             <div class="ds-container">

-               <div class="ds-inline">

-                 <div>

-                   <label for="agmt-cn" class="ds-config-label" title="Agreement name (cn).">Agreement Name</label><input

-                     class="ds-input agmt-form-input" type="text" placeholder="Agreement name" id="agmt-cn" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="nsds5replicahost" class="ds-config-label" title="Agreement name (nsDS5ReplicaHost).">Consumer Host</label><input

-                     class="ds-input agmt-form-input" type="text" placeholder="Consumer hostname" id="nsds5replicahost" name="port" size="40" required>

-                 </div>

-                 <div>

-                   <label for="nsds5replicaport" class="ds-config-label" title="Agreement name (nsDS5ReplicaPort).">Consumer Port</label><input

-                     class="ds-input agmt-form-input" type="text" placeholder="Consumer port number" id="nsds5replicaport" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="nsds5replicabinddn" class="ds-config-label" title="Replication Bind DN (nsDS5ReplicaBindDN).">Replication Bind DN</label><input

-                     class="ds-input agmt-form-input" type="text" autocomplete="username" placeholder="Bind DN" id="nsds5replicabinddn" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="nsds5replicacredentials" class="ds-config-label" title="Replication Bind DN (nsDS5ReplicaCredentials).">Bind DN Password</label><input

-                     class="ds-input agmt-form-input" type="password" autocomplete="new-password" placeholder="Enter password" id="nsds5replicacredentials" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="nsds5replicacredentials-confirm" class="ds-config-label" title="Confirm password">Confirm Password</label><input

-                     class="ds-input agmt-form-input" type="password" autocomplete="new-password" placeholder="Confirm password" id="nsds5replicacredentials-confirm" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="nsds5replicatransportinfo" class="ds-config-label" title="The protocol used to connect to the replica  (nsDS5ReplicaTransportInfo).">Connection Protocol</label><select

-                     class="btn btn-default dropdown ds-agmt-wiz-dropdown" id="nsds5replicatransportinfo">

-                       <option>LDAP</option>

-                       <option>LDAPS</option>

-                       <option>StartTLS</option>

-                     </select>

-                 </div>

-                 <div>

-                   <label for="nsds5replicabindmethod" class="ds-config-label" title="The authentication method (nsDS5ReplicaBindMethod).">Bind Method</label><select

-                     class="btn btn-default dropdown ds-agmt-wiz-dropdown" id="nsds5replicabindmethod">

-                       <option>SIMPLE</option>

-                       <option>SSLCLIENTAUTH</option>

-                       <option>SASL/DIGEST-MD5</option>

-                       <option>SASL/GSSAPI</option>

-                     </select>

-                 </div>

-                 <div id="init-agmt-dropdown">

-                   <label for="init-options" class="ds-config-label">Initialize Consumer</label><select

-                     class="btn btn-default dropdown ds-agmt-wiz-dropdown ds-agmt-init-dropdown" id="init-options">

-                     <option id="init-nothing" value="noinit">Do Not Initialize</option>

-                     <option id="init-online"  value="online-init">Do Online Initialization</option>

-                     <option id="init-offline" value="offline-init">Create Initialization LDIF File</option>

-                   </select>

-                 </div>

-               </div>

-             </div>

- 

-             <!-- Fractional settings -->

-             <p></p>

-             <button class="accordion ds-wiz-accordion" id="frac-accordion" type="button">&#9658 Show Fractional Settings</button>

-             <div class="ds-accordion-panel">

-               <p><b>Excluded Attributes</b></p>

-               <div class="ds-container">

-                 <div>

-                   <form>

-                   <select id="frac-exclude-list" class="ds-fractional-list" name="exclude-attrs" multiple>

-                   </select>

-                   </form>

-                 </div>

-                 <div>

-                   <div>

-                     <p></p>

-                     <input type="button" id="frac-list-add-btn" data-toggle="modal" data-target="#select-attr-form" class="ds-fractional-btn" value="Add Attributes"/>

-                   </div>

-                   <div>

-                     <input type="button" id="frac-list-remove-btn" class="ds-fractional-btn" value="Remove Attributes"/>

-                   </div>

-                 </div>

-               </div>

- 

-               <hr>

-               <p><b>Excluded Attributes From Total Initializations</b></p>

-               <div class="ds-container">

-                 <div>

-                   <form>

-                   <select id="frac-exclude-tot-list" class="ds-fractional-list" name="exclude-attrs" multiple>

-                   </select>

-                   </form>

-                 </div>

-                 <div>

-                   <div>

-                     <p></p>

-                     <input type="button" id="frac-total-list-add-btn" data-toggle="modal" data-target="#select-attr-form" class="ds-fractional-btn" value="Add Attributes"/>

-                   </div>

-                   <div>

-                     <input type="button" id="frac-total-list-remove-btn" class="ds-fractional-btn" value="Remove Attributes"/>

-                   </div>

-                 </div>

-               </div>

- 

-               <hr>

-               <p><b>Strip Attributes</b></p>

-               <div class="ds-container">

-                 <div>

-                   <form>

-                   <select id="frac-strip-list" class="ds-fractional-list" name="exclude-attrs" multiple>

-                   </select>

-                   </form>

-                 </div>

-                 <div>

-                   <div>

-                     <p></p>

-                     <input type="button" id="frac-strip-list-add-btn" data-toggle="modal" data-target="#select-attr-form" class="ds-fractional-btn" value="Add Attributes"/>

-                   </div>

-                   <div>

-                     <input type="button" id="frac-strip-list-remove-btn" class="ds-fractional-btn" value="Remove Attributes"/>

-                   </div>

-                 </div>

-               </div>

-             </div>

- 

-             <!-- Schedule settings -->

- 

-             <button class="accordion ds-wiz-accordion" id="schedule-accordion" type="button">&#9658 Show Scheduling Settings</button>

-             <div class="ds-accordion-panel">

-               <input type="checkbox" class="ds-repl-manager-checkbox" id="agmt-schedule-checkbox" checked><label

-                 for="agmt-schedule-checkbox" class="ds-label"> Always keep directories in sync </label>

-               <div id="agmt-schedule-panel">

-                 <div class="ds-container" id="schedule-settings" hidden>

-                   <div class="ds-fractional-panel">

-                     <hr>

-                     <b>Days to keep replication in sync</b>

-                     <div class="ds-container">

-                       <div class="ds-inline">

-                         <div>

-                           <input type="checkbox" class="ds-agmt-schedule-checkbox" id="schedule-mon" checked><label

-                             for="schedule-mon" class="ds-label"> Mon</label>

-                         </div>

-                         <div>

-                           <input type="checkbox" class="ds-agmt-schedule-checkbox" id="schedule-tue" checked><label

-                             for="schedule-tue" class="ds-label"> Tue </label>

-                         </div>

-                         <div>

-                           <input type="checkbox" class="ds-agmt-schedule-checkbox" id="schedule-wed" checked><label

-                             for="schedule-wed" class="ds-label"> Wed </label>

-                         </div>

-                         <div>

-                           <input type="checkbox" class="ds-agmt-schedule-checkbox" id="schedule-thu" checked><label

-                             for="schedule-thu" class="ds-label"> Thu </label>

-                         </div>

-                       </div>

-                       <div class="ds-divider"></div>

-                       <div class="ds-inline">

-                         <div>

-                           <input type="checkbox" class="ds-agmt-schedule-checkbox" id="schedule-fri" checked><label

-                             for="schedule-fri" class="ds-label"> Fri </label>

-                         </div>

-                         <div>

-                           <input type="checkbox" class="ds-agmt-schedule-checkbox" id="schedule-sat" checked><label

-                             for="schedule-sat" class="ds-label"> Sat </label>

-                         </div>

-                         <div>

-                           <input type="checkbox" class="ds-agmt-schedule-checkbox" id="schedule-sun" checked><label

-                             for="schedule-sun" class="ds-label"> Sun</label>

-                         </div>

-                       </div>

-                     </div>

-                   </div>

-                   <div class="ds-fractional-panel">

-                     <hr>

-                     <b>Replication Time Range</b>

-                     <div class="ds-container">

-                       <div class="ds-inline ds-left-margin">

-                         <div>

-                           <label for="agmt-start-time" class="ds-config-label-med">Start Time</label><input class="timepicker" name="start" id="agmt-start-time" size="4"/>

-                         </div>

-                         <div>

-                           <label for="agmt-start-time" class="ds-config-label-med">End Time</label><input class="timepicker" name="start" id="agmt-end-time" size="4"/>

-                         </div>

-                       </div>

-                     </div>

-                   </div>

-                 </div>

-               </div>

-             </div>

-           </div>

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

-       </div>

-     </div>

-   </div>

- 

- 

-   <!-- Winsync Agreement Wizard  -->

- 

-   <div class="modal fade" id="winsync-agmt-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="winsync-agmt-wizard-title" aria-hidden="true">

-     <div class="modal-dialog">

-       <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="winsync-agmt-wizard-title">Create Winsync Agreement</h4>

-         </div>

-         <div class="modal-body">

-           <form class="form-horizontal">

-             <div class="ds-container">

-               <div class="ds-inline">

-                 <div>

-                   <label for="winsync-agmt-cn" class="ds-config-label" title="Agreement name (cn).">Agreement Name</label><input

-                     class="ds-input" type="text" placeholder="Agreement name" id="winsync-agmt-cn" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="winsync-nsds7windowsdomain" class="ds-config-label" title="Agreement name (nsds7WindowsDomain).">Windows Domain Name</label><input

-                     class="ds-input" type="text" placeholder="Windows Domain Name, example:  mydomain.com" id="winsync-nsds7windowsdomain" size="40" name="name" required>

-                 </div>

-                 <div>

-                   <label for="winsync-nsds5replicahost" class="ds-config-label" title="Agreement name (nsDS5ReplicaHost).">Windows Host</label><input

-                     class="ds-input" type="text" placeholder="Windows hostname" id="winsync-nsds5replicahost" name="port" size="40" required>

-                 </div>

-                 <div>

-                   <label for="winsync-nsds5replicaport" class="ds-config-label" title="Agreement name (nsDS5ReplicaPort).">Windows Port</label><input

-                     class="ds-input" type="text" placeholder="Windows server port number" id="winsync-nsds5replicaport" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="winsync-nsds7windowsreplicasubtree" class="ds-config-label" title="Agreement name (nsds7WindowsReplicaSubtree).">Windows Subtree</label><input

-                     class="ds-input" type="text" placeholder="Active Directory subtree" id="winsync-nsds7windowsreplicasubtree" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="winsync-nsds7directoryreplicasubtree" class="ds-config-label" title="Agreement name (nsds7DirectoryReplicaSubtree).">Directory Server Subtree</label><input

-                     class="ds-input" type="text" placeholder="The local Directory Server subtree" id="winsync-nsds7directoryreplicasubtree" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <input type="checkbox" class="ds-config-checkbox" id="winsync-nsds7newwinusersyncenabled-checkbox" checked><label

-                     for="winsync-nsds7newwinusersyncenabled-checkbox" class="ds-label" title=

-                     "Add new user to Directory Server when it is added to Active Directory (nsds7NewWinUserSyncEnabled)."> Sync New Windows Users</label>

-                 </div>

-                 <div>

-                   <input type="checkbox" class="ds-config-checkbox" id="winsync-nsds7newwingroupsyncenabled-checkbox" checked><label

-                     for="winsync-nsds7newwingroupsyncenabled-checkbox" class="ds-label" title=

-                     "Add new group to Directory Server when it is added to Active Directory (nsds7NewWinGroupSyncEnabled)."> Sync New Windows Groups</label>

-                 </div>

-                 <div id="winsync-init-chbx">

-                   <input type="checkbox" class="ds-config-checkbox" id="winsync-init-checkbox" checked><label

-                     for="winsync-init-checkbox" class="ds-label" title=

-                     "Do a full synchronization after creating winsync agreement."> Initiate Full Synchronization</label>

-                 </div>

-                 <div>

-                   <hr class="ds-hr">

-                   <label for="winsync-nsds5replicabinddn" class="ds-config-label" title="Replication Bind DN (nsDS5ReplicaBindDN).">Replication Bind DN</label><input

-                     class="ds-input" type="text" autocomplete="username" placeholder="Bind DN" id="winsync-nsds5replicabinddn" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="winsync-nsds5replicacredentials" class="ds-config-label" title="Replication Bind DN (nsDS5ReplicaCredentials).">Bind DN Password</label><input

-                     class="ds-input" type="password" autocomplete="new-password" placeholder="Enter password" id="winsync-nsds5replicacredentials" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="winsync-nsds5replicacredentials-confirm" class="ds-config-label" title="Confirm password">Confirm Password</label><input

-                     class="ds-input" type="password"autocomplete="new-password" placeholder="Confirm password" id="winsync-nsds5replicacredentials-confirm" name="name" size="40" required>

-                 </div>

-                 <div>

-                   <label for="winsync-nsds5replicatransportinfo" class="ds-config-label" title="The protocol used to connect to the replica (nsDS5ReplicaTransportInfo).">Connection Protocol</label><select

-                     class="btn btn-default dropdown ds-agmt-wiz-dropdown" id="winsync-nsds5replicatransportinfo">

-                       <option>LDAPS</option>

-                       <option>StartTLS</option>

-                     </select>

-                 </div>

-               </div>

-             </div>

-           </form>

-         </div>

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

-       </div>

-     </div>

-   </div>

- 

- 

-   <!-- CleanAllRUV Form -->

-   <div class="modal fade" id="cleanallruv-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="cleanall-label" aria-hidden="true">

-     <div class="modal-dialog ds-modal">

-       <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 ds-center" id="cleanall-label">Create CleanAllRUV Task</h4>

-         </div>

-         <div class="modal-body">

-           <form class="form-horizontal">

-             <div class="ds-inline">

-               <p>The CleanAllRUV task will clean up traces of a deleted replica from

-                  the database and changelog.  The task follows the replication agreements

-                  so the cleaning task propagates itself to all the replication servers.</p>

-               <hr>

-               <div>

-                 <label for="cleanallruv-suffix" class="ds-cleanallruv-label"><b>Replication Suffix</b></label><select

-                   class="btn btn-default dropdown" id="cleanallruv-suffix">

-                 </select>

-               </div>

-               <p></p>

-               <div>

-                 <label for="cleanallruv-rid" class="ds-cleanallruv-label" title=

-                   "The Replica ID of a deleted replica (replica-id)"><b

-                   >Replica ID</b></label><input type="text" id="cleanallruv-rid" size="5"/>

-               </div>

-               <div>

-                 <input type="checkbox" class="ds-config-checkbox" id="force-clean" checked><label

-                   for="force-clean" class="ds-label" title=

-                   "Clean the replica id regardless if replicas are online or if updates from the deleted replica have been fully replicated. (replica-force-cleaning)"> Force Cleaning</label>

-               </div>

-             </div>

-           </form>

-         </div>

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

-       </div>

-     </div>

-   </div>

- 

- 

- 

- 

-   <!-- Add replication manager Form -->

-   <div class="modal fade" id="add-repl-mgr-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="repl-mgr-label" aria-hidden="true">

-     <div class="modal-dialog">

-       <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-mgr-label">Add Replication Manager</h4>

-         </div>

-         <div class="modal-body">

-           <form class="form-horizontal">

-             <div class="ds-inline">

-               <div>

-                 <label for="add-repl-mgr-dn" class="" title=

-                   "The DN of the replication manager.  The entry should use 'cn' for the RDN, and the entry should be under 'cn=config'.  (nsds5replicabinddn)">

-                   Replication Manager DN</label>

-               </div>

-               <div>

-                 <input type="text" title="The DN of the replication manager entry.  It must use the RDN attribute 'cn', and it must be located under 'cn=config'.  For example: cn=replication manager,cn=config"

-                  id="add-repl-mgr-dn" size="60"/>

-                 <p></p>

-               </div>

-               <div>

-                 <label for="add-repl-pw" class="ds-label-sm" title="Replication manager password (userpassword).">Manager Password</label><input

-                   class="ds-input" type="password" autocomplete="new-password" placeholder="Enter credentials" id="add-repl-pw" name="name" required>

-               </div>

-               <div>

-                 <label for="add-repl-pw-confirm" class="ds-label-sm" title="Confirm password">Confirm Password</label><input

-                   class="ds-input" type="password" autocomplete="new-password" placeholder="Confirm credentials" id="add-repl-pw-confirm" name="name" required>

-               </div>

-             </div>

-           </form>

-         </div>

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

-       </div>

-     </div>

-   </div>

- 

-   <!-- Select an attribute for repl agmt forms -->

-   <div class="modal fade" id="select-attr-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="attr-header" aria-hidden="true">

-     <div class="modal-dialog ds-modal">

-       <div class="modal-content ds-nested-modal">

-         <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="attr-header">Choose Attributes...</h4>

-         </div>

-         <input class="ds-input" type="text" id="attr-form-id" value="" hidden>

-         <div class="modal-body">

-           <form class="form-horizontal">

-             <select id="select-attr-list" class="ds-attr-select" multiple>

-             </select>

-           </form>

-         </div>

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

-       </div>

-     </div>

-   </div>

- 

-   <!-- Enable replication form -->

-   <div class="modal fade" id="enable-repl-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="enable-repl-header" aria-hidden="true">

-     <div class="modal-dialog ds-modal">

-       <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 ds-center" id="enable-repl-header">Enable Replication</h4>

-         </div>

-         <div class="modal-body">

-           <form class="form-horizontal">

-             <div>

-               <label class="ds-config-label-med" for="select-enable-repl-role">Replication Role</Label><select class="btn btn-default dropdown" id="select-enable-repl-role">

-                 <option>Master</option>

-                 <option>Hub</option>

-                 <option>Consumer</option>

-               </select>

-             </div>

-             <div id="repl-rid-form">

-               <label for="nsds5replicaid-form" class="ds-config-label-med" title=

-                 "The unique replica identifier for Master/Supplier.  Please choose a number between 1 and 65534 (nsds5ReplicaId).">

-                 Replica ID</label><input class="ds-input" type="text" id="nsds5replicaid-form" size="4"/>

-             </div>

-             <hr>

-             <div class="ds-inline">

-               <div>

-                 <label id="enable-repl-mgr-title"><b>Replication Authentication</b></label>

-               </div>

-               <div class="ds-left-indent">

-                 <div>

-                   <input id="auth-mgr" type="radio" name="auth-option" value="manager" checked><label class="ds-sm-left-margin" for="auth-mgr"> Replication Manager</label>

-                 </div>

-                 <div>

-                   <input id="auth-group" type="radio" name="auth-option" value="group"><label class="ds-sm-left-margin" for="auth-group"> Replication Bind Group</label>

-                 </div>

-                 <hr>

-                 <div class="ds-left-indent">

-                   <div id="auth-manager-div">

-                     <div>

-                       <label for="enable-repl-mgr-dn" class="ds-label-sm" title=

-                         "The DN of the replication manager.  The DN should use 'cn' for the RDN, 'cn=replication manager,cn=config' (nsds5replicabinddn)">Replication Manager DN</label><input

-                         type="text" title="The DN of the replication manager entry.  It must use the RDN attribute 'cn', and it must be located under 'cn=config'.  For example: cn=replication manager,cn=config"

-                         id="enable-repl-mgr-dn" value="cn=replication manager,cn=config" size="40" class="ds-left-margin" />

-                       <p></p>

-                     </div>

-                     <div>

-                       <label for="enable-repl-pw" class="ds-label-sm" title="Replication manager password (userpassword).">Manager Password</label><input

-                         class="ds-left-margin" size="40" type="password" autocomplete="new-password" placeholder="Enter credentials" id="enable-repl-pw" name="name" required>

-                     </div>

-                     <div>

-                       <label for="enable-repl-pw-confirm" class="ds-label-sm" title="Confirm password">Confirm Password</label><input

-                         class="ds-left-margin" size="40" type="password" autocomplete="new-password" placeholder="Confirm credentials" id="enable-repl-pw-confirm" name="name" required>

-                     </div>

-                   </div>

- 

-                   <div id="auth-group-div" hidden>

-                     <div>

-                       <label for="enable-bindgroupdn" class="ds-label-sm" title=

-                         "The DN of a group that contains members that are allowed to make replication updates (nsDS5ReplicaBindDnGroup).">Bind Group DN</label><input

-                         type="text" id="enable-bindgroupdn" size="40"/>

-                     </div>

-                   </div>

- 

-                 </div>

-               </div>

-             </div>

-           </form>

-         </div>

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

-       </div>

-     </div>

-   </div>

@@ -1,2207 +0,0 @@ 

- var repl_suffix = "";

- var prev_repl_role = "no-repl";

- var prev_role_button;

- var binddn_list_color = "";

- var repl_mgr_table;

- var current_role = "";

- var current_rid = "";

- var repl_agmt_table;

- var repl_winsync_agmt_table;

- var repl_clean_table;

- var repl_agmt_values = {};

- var repl_winsync_agmt_values = {};

- var frac_prefix = "(objectclass=*) $ EXCLUDE";

- var agmt_init_intervals = [];

- var agmt_init_counter = 0;

- var winsync_init_intervals = [];

- var winsync_init_counter = 0;

- 

- // HTML items

- var progress_html = '<p><span class="spinner spinner-xs spinner-inline"></span> Initializing Agreement...</p>';

- 

- var agmt_action_html =

-   '<div class="dropdown">' +

-      '<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">' +

-       ' Choose Action...' +

-       '<span class="caret"></span>' +

-     '</button>' +

-     '<ul class="dropdown-menu ds-agmt-dropdown" role="menu">' +

-       '<li role=""><a class="repl-agmt-btn agmt-edit-btn" href="#">Edit Agreement</a></li>' +

-       '<li role=""><a class="repl-agmt-btn agmt-init-btn" href="#">Initialize Agreement</a></li>' +

-       '<li role=""><a class="repl-agmt-btn agmt-send-updates-btn" href="#">Send Updates Now</a></li>' +

-       '<li role=""><a class="repl-agmt-btn agmt-enable-btn" href="#">Enable/Disable Agreement</a></li>' +

-       '<li role=""><a class="repl-agmt-btn agmt-del-btn" href="#">Delete Agreement</a></li>' +

-     '</ul>' +

-   '</div>';

- 

- var winsync_agmt_action_html =

-   '<div class="dropdown">' +

-      '<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">' +

-       ' Choose Action...' +

-       '<span class="caret"></span>' +

-     '</button>' +

-     '<ul class="dropdown-menu ds-agmt-dropdown" role="menu" aria-labelledby="dropdownMenu2">' +

-       '<li role=""><a class="repl-agmt-btn winsync-agmt-edit-btn" href="#">Edit Agreement</a></li>' +

-       '<li role=""><a class="repl-agmt-btn winsync-agmt-send-updates-btn" href="#">Send/Receives Updates Now</a></li>' +

-       '<li role=""><a class="repl-agmt-btn winsync-agmt-init-btn" href="#">Full Re-synchronization</a></li>' +

-       '<li role=""><a class="repl-agmt-btn winsync-agmt-enable-btn" href="#">Enable/Disable Agreement</a></li>' +

-       '<li role=""><a class="repl-agmt-btn winsync-agmt-del-btn" href="#">Delete Agreement</a></li>' +

-     '</ul>' +

-   '</div>';

- 

- var cleanallruv_action_html =

-   '<button class="btn btn-default abort_cleanallruv_btn" type="button" class="abort-cleanallruv">Abort Task</button>';

- 

- // Attribute to CLI argument mappings

- var repl_attr_map = {

-   'nsds5replicaid': '--replica-id',

-   'nsds5replicapurgedelay': '--repl-purge-delay',

-   'nsds5replicatombstonepurgeinterval': '--repl-tombstone-purge-interval',

-   'nsds5replicaprecisetombstonepurging': '--repl-fast-tombstone-purging',

-   'nsds5replicabinddngroup': '--repl-bind-group',

-   'nsds5replicabinddngroupcheckinterval':  '--repl-bind-group-interval',

-   'nsds5replicaprotocoltimeout': '--repl-protocol-timeout',

-   'nsds5replicabackoffmin': '--repl-backoff-min',

-   'nsds5replicabackoffmax': '--repl-backoff-max',

-   'nsds5replicareleasetimeout': '--repl-release-timeout',

-   'nsds5flags': '',

-   'nsds5replicatype': '',

-   'nsds5replicabinddn': '',

-   'nsslapd-changelogdir': '--cl-dir',

-   'nsslapd-changelogmaxentries': '--max-entries',

-   'nsslapd-changelogmaxage': '--max-age',

-   'nsslapd-changelogcompactdb-interval': '--compact-interval',

-   'nsslapd-changelogtrim-interval': '--trim-interval'

- };

- 

- var repl_cl_attrs = ['nsslapd-changelogdir', 'nsslapd-changelogmaxentries', 'nsslapd-changelogmaxage',

-                      'nsslapd-changelogcompactdb-interval', 'nsslapd-changelogtrim-interval'];

- 

- var repl_attrs = ['nsds5replicaid', 'nsds5replicapurgedelay', 'nsds5replicatombstonepurgeinterval',

-                   'nsds5replicaprecisetombstonepurging', 'nsds5replicabinddngroup',

-                   'nsds5replicabinddngroupcheckinterval', 'nsds5replicaprotocoltimeout', 'nsds5replicabackoffmin',

-                   'nsds5replicabackoffmax', 'nsds5replicareleasetimeout'];

- 

- 

- // Helper functions

- function clear_agmt_wizard () {

-   // Clear input fields and reset dropboxes

-   $('.ds-agmt-schedule-checkbox').prop('checked', false);

-   $('#agmt-schedule-checkbox').prop('checked', true);

-   $("#agmt-start-time").val("00:00");

-   $("#agmt-end-time").val("00:15");

-   $(".ds-agmt-wiz-dropdown").prop('selectedIndex',0);

-   $(".ds-agmt-panel").css('display','none');

-   $(".agmt-form-input").css("border-color", "initial");

-   $(".agmt-form-input").val("");

-   $('#frac-exclude-list').find('option').remove();

-   $('#frac-exclude-tot-list').find('option').remove();

-   $('#frac-strip-list').find('option').remove();

-   $("#select-attr-list").prop('selectedIndex',-1);

-   $("#init-options").prop("selectedIndex", 0);

-   $("#init-agmt-dropdown").show();

-   $("#agmt-wizard-title").html("<b>Create Replication Agreement</b>");

- };

- 

- function clear_enable_repl_form () {

-   $("#nsds5replicaid-form").css("border-color", "initial");

-   $("#nsds5replicaid-form").val("");

-   $("#select-enable-repl-role").prop("selectedIndex", 0);

-   $("#enable-repl-pw").val("");

-   $("#enable-repl-pw-confirm").val("");

-   $("#enable-repl-mgr-dn").val("cn=replication manager,cn=config");

-   $("#enable-repl-mgr-checkbox").prop('checked', false);

-   $("#enable-repl-mgr-passwd").hide();

- }

- 

- function clear_winsync_agmt_wizard() {

-   // Clear out winsync agreement form

-   $("#winsync-agmt-cn").val("");

-   $("#winsync-nsds7windowsdomain").val("");

-   $("#winsync-nsds5replicahost").val("");

-   $("#winsync-nsds5replicaport").val("");

-   $("#winsync-nsds7windowsreplicasubtree").val("");

-   $("#winsync-nsds7directoryreplicasubtree").val("");

-   $("#winsync-nsds7newwinusersyncenabled-checkbox").prop('checked', false);

-   $("#winsync-nsds7newwingroupsyncenabled-checkbox").prop('checked', false);

-   $("#winsync-init-checkbox").prop('checked', false);

-   $("#winsync-init-chbx").show();

-   $("#winsync-nsds5replicabinddn").val("");

-   $("#winsync-nsds5replicacredentials").val("");

-   $("#winsync-nsds5replicacredentials-confirm").val("");

-   $("#winsync-nsds5replicatransportinfo").prop('selectedIndex', 0);

-   $("#winsync-agmt-wizard-title").html("<b>Create Winsync Agreement</b>");

- }

- 

- function clear_cleanallruv_form () {

-   // Clear input fields and reset dropboxes

-   $('#force-clean').prop('checked', true);

-   $("#cleanallruv-rid").val("");

- };

- 

- function clear_repl_mgr_form () {

-   $("#add-repl-pw").val("");

-   $("#add-repl-pw-confirm").val("");

-   $("#add-repl-mgr-dn").val("cn=replication manager,cn=config");

-   $("#add-repl-mgr-checkbox").prop('checked', false);

-   $("#add-repl-mgr-passwd").hide();

- }

- 

- 

- function add_repl_mgr(dn){

-   // First check if manager is set to none

- 	$("#repl-mgr-table tbody").append(

- 		"<tr>"+

- 		"<td class='ds-td'>" + dn +"</td>"+

-     "<td class='ds-center'>"+

-     "<button type='button' class='btn btn-default remove-repl-mgr' title='Remove the manager from the replication configuration'>" +

-     "<span class='glyphicon glyphicon-trash'></span> Remove </button></td>" +

- 		"</tr>");

- };

- 

- 

- function do_agmt_init(suffix, agmt_cn, idx) {

-   var status_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'init-status', '--suffix=' + suffix, '"' + agmt_cn + '"' ];

-   log_cmd('do_agmt_init', 'Get initialization status for agmt', status_cmd);

-   cockpit.spawn(status_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-     var init_stat = JSON.parse(data);

-     if (init_stat == 'Agreement successfully initialized.' ||

-         init_stat == 'Agreement initialization failed.')

-     {

-       // Init is done (good or bad)

-       get_and_set_repl_agmts();

-       clearInterval(agmt_init_intervals[idx]);

-     }

-   });

- }

- 

- function do_winsync_agmt_init(suffix, agmt_cn, idx) {

-   var status_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'init-status', '--suffix=' + suffix, '"' + agmt_cn + '"' ];

-   log_cmd('do_winsync_agmt_init', 'Get initialization status for agmt', status_cmd);

-   cockpit.spawn(status_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-     var init_stat = JSON.parse(data);

-     if (init_stat == 'Agreement successfully initialized.' ||

-         init_stat == 'Agreement initialization failed.')

-     {

-       // Init is done (good or bad)

-       get_and_set_repl_winsync_agmts();

-       clearInterval(agmt_init_intervals[idx]);

-     }

-   });

- }

- 

- function get_and_set_repl_winsync_agmts() {

-   /*

-    * Get the replication agreements for the selected suffix

-    */

-   var suffix = $("#select-repl-winsync-suffix").val();

-   repl_winsync_agmt_table.clear();

- 

-   if (suffix) {

-     console.log("Loading winsync replication agreements...");

-     var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'list', '--suffix=' + suffix ];

-     log_cmd('get_and_set_repl_winsync_agmts', 'Get the winsync agmts', cmd);

-     cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-       var obj = JSON.parse(data);

-       update_progress();

-       for (var idx in obj['items']) {

-         var state = "Enabled";

-         var con_host = "";

-         var con_port = "";

-         var ds_subtree = "";

-         var win_subtree = "";

-         agmt_attrs = obj['items'][idx]['attrs'];

-         var agmt_name = agmt_attrs['cn'][0];

- 

-         // Compute state (enabled by default)

-         if ('nsds5replicaenabled' in agmt_attrs) {

-           if (agmt_attrs['nsds5replicaenabled'][0].toLowerCase() == 'off'){

-             state = "Disabled";

-           }

-         }

-         var ws_agmt_init_status = "Initialized";

-         if ('nsds5replicalastinitstatus' in agmt_attrs &&

-             agmt_attrs['nsds5replicalastinitstatus'][0] != "")

-         {

-           ws_agmt_init_status = agmt_attrs['nsds5replicalastinitstatus'][0];

-           if (ws_agmt_init_status == "Error (0) Total update in progress" ||

-               ws_agmt_init_status == "Error (0)")

-           {

-             ws_agmt_init_status = progress_html;

-             var ws_interval_agmt_name = agmt_name;

-             var ws_init_status_interval = setInterval(function() {

-               var status_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'init-status', '--suffix=' + suffix, '"' + ws_interval_agmt_name + '"' ];

-               log_cmd('get_and_set_repl_winsync_agmts', 'Get initialization status for winsync agmt', status_cmd);

-               cockpit.spawn(status_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-                 var init_stat = JSON.parse(data);

-                 if (init_stat == 'Agreement successfully initialized.' ||

-                     init_stat == 'Agreement initialization failed.')

-                 {

-                   // Init is done (good or bad)

-                   get_and_set_repl_winsync_agmts();

-                   clearInterval(ws_init_status_interval);

-                 }

-               });

-             }, 2000);

-           } else if (ws_agmt_init_status == "Error (0) Total update succeeded") {

-             ws_agmt_init_status = "Initialized";

-           }

-         } else if (agmt_attrs['nsds5replicalastinitstart'][0] == "19700101000000Z"){

-           ws_agmt_init_status = "Not initialized";

-         }

- 

-         repl_winsync_agmt_table.row.add( [

-           agmt_attrs['cn'][0],

-           agmt_attrs['nsds5replicahost'][0],

-           agmt_attrs['nsds5replicaport'][0],

-           state,

-           agmt_attrs['nsds5replicalastupdatestatus'][0],

-           ws_agmt_init_status,

-           winsync_agmt_action_html

-         ] ).draw( false );

-         console.log("Finished loading winsync replication agreements.");

-       }

-     });

-   } // suffix

- }

- 

- 

- function get_and_set_repl_agmts () {

-   /*

-    * Get the replication agreements for the selected suffix

-    */

-   var suffix = $("#select-repl-agmt-suffix").val();

-   if (suffix) {

-     console.log("Loading replication agreements...");

-     var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'list', '--suffix=' + suffix ];

-     log_cmd('get_and_set_repl_agmts', 'Get replication agreements', cmd);

-     cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-       repl_agmt_table.clear().draw();

-       var obj = JSON.parse(data);

-       update_progress();

-       for (var idx in obj['items']) {

-         agmt_attrs = obj['items'][idx]['attrs'];

-         var agmt_name = agmt_attrs['cn'][0];

-         var state = "Enabled";

-         var update_status = "";

-         var agmt_init_status = "Initialized";

- 

-         // Compute state (enabled by default)

-         if ('nsds5replicaenabled' in agmt_attrs) {

-           if (agmt_attrs['nsds5replicaenabled'][0].toLowerCase() == 'off'){

-             state = "Disabled";

-           }

-         }

- 

-         // Check for status msgs

-         if ('nsds5replicalastupdatestatus' in agmt_attrs) {

-           update_status = agmt_attrs['nsds5replicalastupdatestatus'][0];

-         }

-         if ('nsds5replicalastinitstatus' in agmt_attrs &&

-             agmt_attrs['nsds5replicalastinitstatus'][0] != "")

-         {

-           agmt_init_status = agmt_attrs['nsds5replicalastinitstatus'][0];

-           if (agmt_init_status == "Error (0) Total update in progress" ||

-               agmt_init_status == "Error (0)")

-           {

-             agmt_init_status = progress_html;

-             var interval_agmt_name = agmt_name;

-             var init_idx = agmt_init_counter;

-             agmt_init_counter += 1;

-             agmt_init_intervals[init_idx] = setInterval( do_agmt_init, 2000, suffix, interval_agmt_name, init_idx);

-           } else if (agmt_init_status == "Error (0) Total update succeeded") {

-             agmt_init_status = "Initialized";

-           }

-         } else if (agmt_attrs['nsds5replicalastinitstart'][0] == "19700101000000Z"){

-           agmt_init_status = "Not initialized";

-         }

- 

-         // Update table

-         repl_agmt_table.row.add( [

-           agmt_attrs['cn'][0],

-           agmt_attrs['nsds5replicahost'][0],

-           agmt_attrs['nsds5replicaport'][0],

-           state,

-           update_status,

-           agmt_init_status,

-           agmt_action_html

-         ] ).draw( false );

-       }

-       console.log("Finished loading replication agreements.");

-     }).fail(function () {

-       repl_agmt_table.clear().draw();

-     });

-   } // suffix

- 

-   // Load fractional replication agreement attr list here

-   var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket', 'schema', 'list'];

-   log_cmd('get_and_set_repl_agmts', 'Get all schema objects', cmd);

-   cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(schema_data) {

-     var schema_json = JSON.parse(schema_data);

-     load_schema_objects_to_select('attributetypes', 'select-attr-list', schema_json);

-   }).fail(function(oc_data) {

-       console.log("Get all schema objects failed: " + oc_data.message);

-       check_inst_alive(1);

-   });

- }

- 

- 

- function get_and_set_cleanallruv() {

-   console.log("Loading replication tasks...");

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

-     update_progress();

-     repl_clean_table.clear().draw();

-     for (var idx in tasks['items']) {

-       task_attrs = tasks['items'][idx]['attrs'];

-       // Update table

-       var abort_btn = cleanallruv_action_html;

-       if (task_attrs['nstaskstatus'][0].includes('Successfully cleaned rid') ){

-         abort_btn = "<i>Task Complete</i>";

-       } else if (task_attrs['nstaskstatus'][0].includes('Task aborted for rid') ){

-          abort_btn = "<i>Task Aborted</i>";

-       }

-       repl_clean_table.row.add( [

-           task_attrs['cn'][0],

-           get_date_string(task_attrs['createtimestamp'][0]),

-           task_attrs['replica-base-dn'][0],

-           task_attrs['replica-id'][0],

-           task_attrs['nstaskstatus'][0],

-           abort_btn

-         ] );

-     }

-     repl_clean_table.draw(false);

-     console.log("Finished loading replication tasks.");

-   });

- }

- 

- 

- function get_and_set_repl_config () {

-   var suffix = $("#select-repl-cfg-suffix").val();

- 

-   if (suffix) {

-     $("#nsds5replicaid").css("border-color", "initial");

-     repl_config_values = {};

-     console.log("Loading replication configuration...");

- 

-     var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'get', '--suffix=' + suffix ];

-     log_cmd('get_and_set_repl_config', 'Get replication configuration', cmd);

-     cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-       var repl = JSON.parse(data);

-       var repl_type;

-       var repl_flags;

-       var manager = false;

-       $('#repl-mgr-table').find("tr:gt(0)").remove();

-       $(".ds-cfg").val("");

-       $("#nsds5replicaprecisetombstonepurging").prop('checked', false);

-       update_progress();

- 

-       // Set configuration and the repl manager table

-       for (var attr in repl['attrs']) {

-         var vals = repl['attrs'][attr];

-         attr = attr.toLowerCase();

- 

-         if (attr in repl_attr_map) {

-           if (attr == "nsds5replicabinddn") {

-             // update manager table

-             for (var val_idx in vals){

-               add_repl_mgr(vals[val_idx]);

-               manager = true;

-             }

-           } else if (attr == "nsds5replicatype") {

-             repl_type = vals[0];

-           } else if (attr == "nsds5flags") {

-             repl_flags = vals[0];

-           } else {

-             // Regular attribute

-             if (vals[0] == "on") {

-               $("#" + attr).prop('checked', true);

-               $("#" + attr).trigger('change');

-             } else if (vals[0] == "off") {

-               $("#" + attr).prop('checked', false);

-               $("#" + attr).trigger('change');

-             }

-             $("#" + attr ).val(vals[0]);

-             repl_config_values[attr] = vals[0];

-           }

-         }

-       }

-       if (!manager) {

-         // Add an empty row to define the table

-         $("#repl-mgr-table tbody").append(

- 		      "<tr>"+

- 		      "<td class='ds-td'>None</td>"+

-           "<td></td>" +

-           "</tr>");

-       }

- 

-       // Set the replica role

-       if (repl_type == "3"){

-         $("#select-repl-role").val("Master");

-         current_role = "Master";

-         $("#nsds5replicaid").show();

-         $("#replicaid-label").show();

-       } else {

-         $("#nsds5replicaid").hide();

-         $("#replicaid-label").hide();

-         if (repl_flags == "1"){

-           $("#select-repl-role").val("Hub");

-           current_role = "Hub";

-         } else {

-           $("#select-repl-role").val("Consumer");

-           current_role = "Consumer";

-         }

-       }

-       current_rid = $("#nsds5replicaid").val();

- 

-       // Show the page (in case it was hidden)

-       $("#ds-repl-enabled").hide();

-       $("#repl-config-content").show();

-       load_repl_suffix_dropdowns();

- 

-       console.log("Finished loading replication configuration.");

-     }).fail(function(data) {

-       // No replication

-       current_role = "Disabled";

-       $("#repl-config-content").hide();

-       $("#ds-repl-enabled").show();

-       load_repl_suffix_dropdowns();

-     });

-   } else {

-     // No Suffix

-     $("#repl-config-content").hide();

-     $("#ds-repl-enabled").hide();

-   }

- 

-   // Do the changelog settings

-   $("#cl-create-div").show();

-   $("#cl-del-div").hide();

-   var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'get-changelog'];

-   log_cmd('get_and_set_repl_config', 'Get replication changelog', cmd);

-   cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-     $(".ds-cl").val("");  // Clear form

-     var cl = JSON.parse(data);

-     repl_cl_values = {};

-     for (var attr in cl['attrs']) {

-       var val = cl['attrs'][attr][0];

-       attr = attr.toLowerCase();

-       $("#" + attr ).val(val);

-       $("#cl-create-div").hide();

-       $("#cl-del-div").show();

-       repl_cl_values[attr] = val;

-     }

-   }).fail(function() {

-     // No changelog, clear the form

-     $(".ds-cl").val("");

-   });

- }

- 

- 

- function save_repl_config(suffix, ignore_rid) {

-   /*

-    * Check for changes in the replication settings

-    */

-   var set_repl_values = {};

-   var set_cl_values = {};

-   var arg_list = [];

-   for (var attr in repl_attrs) {

-     attr = repl_attrs[attr];

-     var val = "";

-     if ( $("#" + attr).is(':checkbox')) {

-       if ( $("#" + attr).is(":checked")) {

-         val = "on";

-       } else {

-         val = "off";

-       }

-     } else {

-       val = $("#" + attr).val();

-     }

-     var prev_val = "";

- 

-     if (attr in repl_config_values) {

-       prev_val = repl_config_values[attr];

-     }

- 

-     if (val != prev_val) {

-       if (attr == "nsds5replicaid"){

-         // skip it since we are doing a promotion

-         continue;

-       }

-       // Handle checkbox input

-       if ( $("#" + attr).is(':checkbox')) {

-         if ( $("#" + attr).is(":checked")) {

-           arg_list.push(repl_attr_map[attr] + "=" + "on" );

-         } else {

-           // Not checked

-           arg_list.push(repl_attr_map[attr] + "=" + "off" );

-         }

-       } else {

-         // Regular input

-         if (val != "") {

-           // Regular setting - add to the list

-           arg_list.push(repl_attr_map[attr] + "=" + val );

-         } else {

-           // removed

-           arg_list.push(repl_attr_map[attr] + "=");

-         }

-       }

-       set_repl_values[attr] = val;

-     }

-   }

- 

-   /*

-    * Check for changes in the changelog settings

-    */

-   var arg_cl_list = [];

-   for (var attr in repl_cl_attrs) {

-     attr = repl_cl_attrs[attr];

-     var val = $("#" + attr).val();

-     var prev_val = "";

-     if (attr in repl_cl_values) {

-       prev_val = repl_cl_values[attr];

-     }

-     if (val != prev_val) {

-       // we have a difference

-       if (val != "") {

-         // Regular setting -add to the list

-         arg_cl_list.push(repl_attr_map[attr] + "=" + val);

-       } else {

-         // removed

-         arg_cl_list.push(repl_attr_map[attr] + "=");

-       }

-       set_cl_values[attr] = val;

-     }

-   }

- 

-   /*

-    * Save repl config settings

-    */

-   if (arg_list.length > 0){

- 

-     var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'set', '--suffix=' + suffix ];

-     cmd = cmd.concat(arg_list);

-     log_cmd('save_repl_config', 'Set replication configuration', cmd);

-     cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-       popup_success('Saved replication configuration');

-       for (var key in set_repl_values) {

-         // Update current in memory values

-         repl_config_values[key] = set_repl_values[key];

-       }

-       /*

-        * Save changelog settings

-        */

-       if (arg_cl_list.length > 0){

-         var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication' ,'set-changelog'];

-         cmd = cmd.concat(arg_cl_list);

-         log_cmd('save_repl_config', 'Set changelog configuration', cmd);

-         cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-           popup_success('Saved changelog configuration');

-           for (var key in set_cl_values) {

-             // Update current in memory values

-             repl_cl_values[key] = set_cl_values[key];

-           }

-           get_and_set_repl_config();

-         }).fail(function(data) {

-           get_and_set_repl_config();

-           popup_err("Failed to save changelog configuration", data.message);

-         });

-       } else {

-         // No changelog changes, so we're done, refresh the settings

-         get_and_set_repl_config();

-       }

-     }).fail(function(data) {

-       // Restore prev values

-       get_and_set_repl_config();

-       popup_err("Failed to set replication configuration", data.message);

-     });

-   } else if (arg_cl_list.length > 0) {

-     /*

-      * Only changelog settings need to be applied

-      */

-     var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication' ,'set-changelog'];

-     cmd = cmd.concat(arg_cl_list);

-     log_cmd('save_repl_config', 'Set changelog configuration', cmd);

-     cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-       popup_success('Saved changelog configuration');

-       for (var key in set_cl_values) {

-         // Update current in memory values

-         repl_cl_values[key] = set_cl_values[key];

-       }

-       get_and_set_repl_config();

-     }).fail(function(data) {

-       get_and_set_repl_config();

-       popup_err("Failed to save changelog configuration", data.message);

-     });

-   }

- }

- 

- 

- /*

-  * Load the replication page, and set the handlers

-  */

- $(document).ready( function() {

-   $("#replication-content").load("replication.html", function () {

-         // Set up agreement table

-     repl_agmt_table = $('#repl-agmt-table').DataTable( {

-       "paging": true,

-       "bAutoWidth": false,

-       "dom": '<"pull-left"f><"pull-right"l>tip',

-       "lengthMenu": [ 10, 25, 50, 100],

-       "language": {

-         "emptyTable": "No agreements configured",

-         "search": "Search Agreements"

-       },

-       "columnDefs": [ {

-         "targets": 6,

-         "orderable": false

-       } ],

-       "columns": [

-         { "width": "20%" },

-         { "width": "20%" },

-         { "width": "50px" },

-         { "width": "50px" },

-         { "width": "20%" },

-         { "width": "20%" },

-         { "width": "130px" }

-       ],

-     });

- 

-     // Set up windows sync agreement table

-     repl_winsync_agmt_table = $('#repl-winsync-agmt-table').DataTable( {

-       "paging": true,

-       "bAutoWidth": false,

-       "dom": '<"pull-left"f><"pull-right"l>tip',

-       "lengthMenu": [ 10, 25, 50, 100],

-       "language": {

-         "emptyTable": "No winsync agreements configured",

-         "search": "Search Agreements"

-       },

-       "columnDefs": [ {

-         "targets": 6,

-         "orderable": false

-       } ]

-     });

- 

-     // Set up CleanAllRUV Table

-     repl_clean_table = $('#repl-clean-table').DataTable( {

-       "paging": true,

-       "searching": false,

-       "bAutoWidth": false,

-       "dom": 'B<"pull-left"f><"pull-right"l>tip',

-       buttons: [

-             {

-                 text: 'Refresh Task List',

-                 action: function ( e, dt, node, config ) {

-                   console.log("hmmm");

-                   get_and_set_cleanallruv();

-                 }

-             }

-         ],

-       "lengthChange": false,

-       "language": {

-         "emptyTable": "No CleanAllRUV tasks",

-       },

-       "columnDefs": [ {

-         "targets": 5,

-         "orderable": false

-       } ]

-     });

- 

-     binddn_list_color = $("#repl-managers-list").css("border-color");

- 

-     // Load existing replication config (if any), set role, etc

- 

-     $("#schedule-settings").hide();

- 

-     $("#repl-config-btn").on("click", function() {

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

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

-       $("#repl-config").show();

-     });

- 

-     $("#repl-agmts-btn").on("click", function() {

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

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

-       $("#repl-agmts").show();

-     });

- 

-     $("#repl-winsync-btn").on("click", function() {

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

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

-       $("#repl-winsync").show();

-     });

-     $("#repl-tasks-btn").on("click", function() {

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

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

-       $("#repl-cleanallruv").show();

-     });

- 

-     $("#select-repl-cfg-suffix").on("change", function() {

-       get_and_set_repl_config();

-     });

- 

-     $("#select-repl-agmt-suffix").on("change", function() {

-       get_and_set_repl_agmts();

-     });

- 

-     $("#select-repl-winsync-suffix").on("change", function() {

-       get_and_set_repl_winsync_agmts();

-     });

- 

-     $("#select-repl-role").on("change", function() {

-       var new_role = $(this).val();

-       if (new_role == "Master"){

-         if (current_role != new_role) {

-           // Reset replica ID for a new master

-           $("#nsds5replicaid").val("0");

-         }

-         $("#nsds5replicaid").show();

-         $("#replicaid-label").show();

-         $("#repl-config-content").show();

-       } else {

-         $("#nsds5replicaid").hide();

-         $("#replicaid-label").hide();

-         $("#repl-config-content").show();

-         $("#nsds5replicaid").css("border-color", "initial");

-       }

-     });

- 

-     $("#enable-repl-btn").on('click', function () {

-       clear_enable_repl_form();

-     });

- 

-     /*

-      * Enable replication - select role dynamics

-      */

-     $("#select-enable-repl-role").on("change", function() {

-       var new_role = $(this).val();

-       if (new_role == "Master"){

-         $("#repl-rid-form").show();

-       } else {

-         $("#repl-rid-form").hide();

-       }

-     });

-     /*

-      * Enable replication - save it

-      */

-     $("#enable-repl-save").on('click', function () {

-       var suffix = $("#select-repl-cfg-suffix").val();

-       var role = $("#select-enable-repl-role").val();

-       var repl_dn = $("#enable-repl-mgr-dn").val();

-       var repl_pw = $("#enable-repl-pw").val();

-       var repl_pw_confirm = $("#enable-repl-pw-confirm").val();

-       var repl_group = $("#enable-bindgroupdn").val();

-       var cmd = [];

- 

-       // Validate all the inputs

-       if (role == "Master") {

-         // Master role, get and valid replica id

-         var rid = $("#nsds5replicaid-form").val();

-         if (rid == ""){

-           $("#nsds5replicaid-form").css("border-color", "red");

-           popup_msg("Missing Replica ID",

-                     "A Master replica requires a unique identifier.  " +

-                     "Please enter a value for <b>Replica ID</b> between 1 and 65534");

-           return;

-         }

-         if (valid_num(rid)){

-           if (rid < 1 || rid >= 65535){

-             $("#nsds5replicaid-form").css("border-color", "red");

-             popup_msg("Invalid Replica ID",

-                       "A Master replica requires a unique identifier.  " +

-                       "Please enter a value for <b>Replica ID</b> between 1 and 65534");

-             return;

-           }

-         } else {

-           $("#nsds5replicaid-form").css("border-color", "red");

-           popup_msg("Replica ID is not a number",

-                     "A Master replica requires a unique identifier.  " +

-                     "Please enter a value for <b>Replica ID</b> between 1 and 65534");

-           return;

-         }

-         $("#nsds5replicaid-form").css("border-color", "initial");

-         cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'enable',

-                '--suffix=' + suffix, '--role=' + role, '--replica-id=' + rid];

-       } else {

-         // Hub or Consumer - must have a bind dn/group

-         if (repl_dn == "" && repl_group == ""){

-           popup_msg("Attention!", "Replication Manager or Replication Bind Group is required");

-           return;

-         }

-         cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'enable',

-                '--suffix=' + suffix, '--role=' + role];

-       }

- 

-       if (repl_dn != ''){

-         if (repl_pw != repl_pw_confirm) {

-           popup_msg("Attention!", "Passwords do not match");

-           $("#enable-repl-pw").val("");

-           $("#enable-repl-pw-confirm").val("");

-           return;

-         }

-         if (!valid_dn(repl_dn)) {

-           popup_msg("Attention!", "Invalid DN for Replication Manager");

-           return;

-         }

-         cmd.push.apply(cmd, ['--bind-dn=' + repl_dn]);

-         cmd.push.apply(cmd, ['--bind-passwd=' + repl_pw]);

-       }

-       if (repl_group != ""){

-         if (!valid_dn(repl_group)){

-           popup_msg("Attention!", "Invalid DN for Replication Bind Group");

-           return;

-         }

-         cmd.push.apply(cmd, ['--bind-group-dn=' + repl_group]);

-       }

- 

-       // Enable replication finally

-       log_cmd('#enable-repl-save (click)', 'Enable replication', cmd);

-       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-         // Replication has been enabled

-         popup_success("Successfully enabled replication");

-         $("#enable-repl-form").modal('toggle');

-         get_and_set_repl_config();

-       }).fail(function(data) {

-         // Undo what we have done

-         popup_err("Failed to enable replication", data.message);

-         var disable_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'disable', '--suffix=' + suffix ];

-         log_cmd('#enable-repl-save (click)', 'Disable replication after error', disable_cmd);

-         cockpit.spawn(disable_cmd, { superuser: true, "err": "message", "environ": [ENV]}).always(function(data) {

-           get_and_set_repl_config();

-           $("#enable-repl-form").modal('toggle');

-         });

-       });

-     });

- 

-     /*

-      * Disable replication

-      */

-     $("#disable-repl-btn").on('click', function () {

-       var suffix = $("#select-repl-cfg-suffix").val();

-       popup_confirm("Are you sure you want to disable replication?  This will remove all your replication agreements and can not be undone!", "Confirmation", function (yes) {

-         if (yes) {

-           var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'disable', '--suffix=' + suffix ];

-           log_cmd('#disable-repl-btn (click)', 'Disable replication', cmd);

-           cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-             current_role = "Disabled";

-             $("#repl-config-content").hide();

-             popup_success('Successfully disabled replication');

-             get_and_set_repl_config();

-           }).fail(function(data) {

-             popup_err("Failed to disable replication", data.message);

-             get_and_set_repl_config();

-           });

-         }

-       });

-     });

- 

-     /*

-      * Save replication configuration

-      */

-     $("#repl-config-save").on('click', function () {

-       var suffix = $("#select-repl-cfg-suffix").val();

-       var rid = $("#nsds5replicaid").val();

- 

-       if (suffix) {

-         /*

-          * Did we config change, promote, or demote this replica?

-          */

-         var new_role = $("#select-repl-role").val();

-         if (new_role != current_role) {

-           /*

-            * Promote/demote the replica

-            */

-           popup_confirm("Are you sure you want to change the <i>replication role</i> to \"<b>" + new_role + "</b>\"?", "Confirmation", function (yes) {

-             if (yes) {

-               if (new_role == "Master"){

-                 /*

-                  * Promote to Master

-                  */

-                 if ( !valid_num(rid) ) {

-                   popup_msg("Invalid Replica ID",

-                             "A Master replica requires a unique numerical identifier.  Please enter a value for <b>Replica ID</b> between 1 and 65534");

-                   get_and_set_repl_config();

-                   return;

-                 }

-                 var rid_num = parseInt(rid, 10);

-                 if (rid_num < 1 || rid_num >= 65535){

-                   popup_msg("Missing Required Replica ID",

-                             "A Master replica requires a unique numerical identifier.  Please enter a value for <b>Replica ID</b> between 1 and 65534");

-                   get_and_set_repl_config();

-                   return;

-                 }

-                 var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'promote',

-                            '--suffix=' + suffix, "--newrole=" + new_role, "--replica-id=" + rid];

-                 log_cmd('#repl-config-save (click)', 'Promote replica to Master', cmd);

-                 cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-                   current_role = "Master";;

-                   popup_success('Successfully promoted replica to a <b>Master</b>');

-                   get_and_set_repl_config();

-                   save_repl_config(suffix, true);

-                 }).fail(function(data) {

-                   popup_err("Failed to promote replica to a Master", data.message);

-                   get_and_set_repl_config();

-                 });

-               } else if (new_role == "Hub" && current_role == "Master"){

-                 /*

-                  * Demote to Hub, but first check that we have a replication manager

-                  */

-                 var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'get', '--suffix=' + suffix ];

-                 log_cmd('get_and_set_repl_config', 'Get replication configuration', cmd);

-                 cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-                   var repl = JSON.parse(data);

-                   var manager = false;

-                   for (var attr in repl['attrs']) {

-                     if (attr.toLowerCase() == "nsds5replicabinddn") {

-                       manager = true;

-                       break;

-                     }

-                   }

-                   if (manager == false) {

-                     popup_msg("Missing Required Setting",

-                               "You must create a replication manager before you can demote this replica to a Hub");

-                     return;

-                   }

-                   var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'demote',

-                              '--suffix=' + suffix, "--newrole=" + new_role];

-                   log_cmd('#repl-config-save (click)', 'Demote replica to Hub', cmd);

-                   cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-                     current_role = "Hub";

-                     popup_success('Successfully demoted replica to a <b>Hub</b>');

-                     save_repl_config(suffix, true);

-                   }).fail(function(data) {

-                     popup_err("Failed to demote replica to a Hub", data.message);

-                     get_and_set_repl_config();

-                   });

-                 });

-               } else if (new_role == "Hub" && current_role == "Consumer"){

-                 /*

-                  * Promote to Hub

-                  */

-                 var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'promote',

-                            '--suffix=' + suffix, "--newrole=" + new_role];

-                 log_cmd('#repl-config-save (click)', 'Promote replica to Hub ', cmd);

-                 cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-                   current_role = "Hub";;

-                   popup_success('Successfully promoted replica to a <b>Hub</b>');

-                   save_repl_config(suffix, true);

-                 }).fail(function(data) {

-                   popup_err("Failed to promote replica to a Hub", data.message);

-                   get_and_set_repl_config();

-                 });

-               } else {

-                 /*

-                  * Demote to Consumer, but first confirm we have a replication manager

-                  */

-                 var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'get', '--suffix=' + suffix ];

-                 log_cmd('get_and_set_repl_config', 'Get replication configuration', cmd);

-                 cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-                   var repl = JSON.parse(data);

-                   var manager = false;

-                   for (var attr in repl['attrs']) {

-                     if (attr.toLowerCase() == "nsds5replicabinddn") {

-                       manager = true;

-                       break;

-                     }

-                   }

-                   if (manager == false) {

-                     popup_msg("Missing Required Setting",

-                               "You must create a replication manager before you can demote this replica to a Consumer.");

-                     return;

-                   }

-                   var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'demote',

-                              '--suffix=' + suffix, "--newrole=" + new_role];

-                   log_cmd('#repl-config-save (click)', 'Demote replication to Consumer', cmd);

-                   cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-                     current_role = "Consumer";

-                     popup_success('Successfully demoted replica to a <b>Consumer</b>');

-                     save_repl_config(suffix, true);

-                   }).fail(function(data) {

-                     popup_err("Failed to demote replica to a Consumer", data.message);

-                     get_and_set_repl_config();

-                   });

-                 });

-               }

-             } else {

-               // Not changing the role - reset the dropdown

-               $("#select-repl-role").val(current_role);

-               get_and_set_repl_config();

-             }

-           }); // popup_confirm

-         } else {

-           /*

-            * We did NOT promote/demote, etc.  This was just a configuration change...

-            */

-           save_repl_config(suffix, false);

-         }

-       } // Suffix

-     });

- 

-     // Create changelog

-     $("#create-cl-btn").on('click', function () {

-       var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication' ,'create-changelog'];

-       log_cmd('#create-cl-btn (click)', 'Create replication changelog', cmd);

-       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-         get_and_set_repl_config();

-         popup_success('Successfully created replication changelog');

-       }).fail(function(data) {

-         get_and_set_repl_config();

-         popup_err("Failed to create replication changelog", data.message);

-       });

-     });

- 

-     // Remove changelog

-     $("#delete-cl-btn").on('click', function () {

-       popup_confirm("Are you sure you want to delete the replication changelog as it will break all the existing agreements?", "Confirmation", function (yes) {

-         if (yes) {

-           var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication' ,'delete-changelog'];

-           log_cmd('#delete-cl-btn (click)', 'Delete replication changelog', cmd);

-           cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-             get_and_set_repl_config();

-             popup_success('Successfully removed replication changelog');

-           }).fail(function(data) {

-             get_and_set_repl_config();

-             popup_err("Failed to remove replication changelog", data.message);

-           });

-         }

-       });

-     });

- 

-     // Save Repl Agreement Wizard

-     $("#agmt-save").on("click", function() {

-       // Get all the settings

-       var suffix = $("#select-repl-agmt-suffix").val();

-       var cmd = [];

-       var cmd_args = [];

-       var param_err = false;

-       var agmt_name = $("#agmt-cn").val();

-       var agmt_host = $("#nsds5replicahost").val();

-       var agmt_port = $("#nsds5replicaport").val();

-       var agmt_bind = $("#nsds5replicabinddn").val();

-       var agmt_bindpw = $("#nsds5replicacredentials").val();

-       var agmt_bindpw_confirm = $("#nsds5replicacredentials-confirm").val();

-       var agmt_conn = $("#nsds5replicatransportinfo").val();

-       var agmt_method = $("#nsds5replicabindmethod").val();

-       var agmt_schedule = "";

-       var agmt_init = $("#init-options").val();

-       var agmt_exclude = "";

-       var agmt_tot_exclude = "";

-       var agmt_strip = "";

-       var editing = false;

-       var init_replica = false;

- 

-       if ($("#agmt-wizard-title").text().includes('Edit') ) {

-         editing = true;

-       }

- 

-       // Check required settings

-       if ( agmt_port == "") {

-         $("#nsds5replicaport").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#nsds5replicaport").css("border-color", "initial");

-         cmd_args.push("--port=" + agmt_port);

-       }

-       if ( agmt_host == "") {

-         $("#nsds5replicahost").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#nsds5replicahost").css("border-color", "initial");

-         cmd_args.push('--host=' + agmt_host);

-       }

-       if ( agmt_conn == "") {

-         $("#nsds5replicatransportinfo").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#nsds5replicatransportinfo").css("border-color", "initial");

-         cmd_args.push('--conn-protocol=' + agmt_conn);

-       }

-       if ( agmt_method == "") {

-         $("#nsds5replicabindmethod").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#nsds5replicabindmethod").css("border-color", "initial");

-         cmd_args.push('--bind-method=' + agmt_method);

-       }

-      if ( agmt_bind == "") {

-         $("#nsds5replicabinddn").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#nsds5replicabinddn").css("border-color", "initial");

-         cmd_args.push('--bind-dn=' + agmt_bind);

-       }

-       if (agmt_name == "") {

-         $("#agmt-cn").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#agmt-cn").css("border-color", "initial");

-         cmd_args.push('"' + agmt_name + '"');

-       }

-       if (param_err ){

-         popup_msg("Error", "Missing required parameters");

-         return;

-       }

- 

-       /*

-        * Handle the optional settings

-        */

-       $("#frac-exclude-list option").each(function() {

-         agmt_exclude += $(this).val() + " ";

-       });

-       $("#frac-exclude-tot-list option").each(function() {

-         agmt_tot_exclude += $(this).val() + " ";

-       });

-       $("#frac-strip-list option").each(function() {

-         agmt_strip += $(this).val() + " ";

-       });

- 

-       if (agmt_bindpw != agmt_bindpw_confirm) {

-         popup_msg("Attention!", "Passwords do not match");

-         return;

-       }

- 

-       // Bind Password

-       if (!editing ){

-         if (agmt_bindpw != "") {

-           cmd_args.push('--bind-passwd=' + agmt_bindpw);

-         }

-       } else {

-         if ( !('nsds5replicacredentials' in repl_agmt_values) ||

-              agmt_bindpw != repl_agmt_values['nsds5replicacredentials'])

-         {

-           cmd_args.push('--bind-passwd=' + agmt_bindpw);

-         }

-       }

-       // Frac attrs

-       agmt_exclude = agmt_exclude.trim();

-       if (!editing) {

-         if (agmt_exclude != "") {

-           cmd_args.push('--frac-list='+ agmt_exclude);

-         }

-       } else {

-         if ( !('nsds5replicatedattributelist' in repl_agmt_values) ||

-             agmt_exclude != repl_agmt_values['nsds5replicatedattributelist'].replace(frac_prefix, ""))

-         {

-           cmd_args.push('--frac-list=' + frac_prefix + ' ' + agmt_exclude);

-         }

-       }

-       // Frac total attr

-       agmt_tot_exclude = agmt_tot_exclude.trim();

-       if (!editing) {

-         if (agmt_tot_exclude != "") {

-           cmd_args.push('--frac-list-total='+ agmt_tot_exclude);

-         }

-       } else {

-         if ( !('nsds5replicatedattributelisttotal' in repl_agmt_values) ||

-              agmt_tot_exclude != repl_agmt_values['nsds5replicatedattributelisttotal'].replace(frac_prefix, ""))

-         {

-           cmd_args.push('--frac-list-total=' + frac_prefix + ' ' + agmt_tot_exclude);

-         }

-       }

-       // Strip attrs

-       agmt_strip = agmt_strip.trim();

-       if (!editing) {

-         if (agmt_strip != "") {

-           cmd_args.push('--strip-list='+ agmt_strip);

-         }

-       } else {

-         if ( !('nsds5replicastripattrs' in repl_agmt_values) ||

-              agmt_strip != repl_agmt_values['nsds5replicastripattrs'] )

-         {

-           cmd_args.push('--strip-list='+ agmt_strip);

-         }

-       }

- 

-       if ( !($("#agmt-schedule-checkbox").is(":checked")) ){

-         agmt_start = $("#agmt-start-time").val().replace(':','');

-         agmt_end = $("#agmt-end-time").val().replace(':','');

- 

-         if (agmt_start == agmt_end) {

-           popup_msg("Error", "The replication start and end times can not behte same");

-           return;

-         }

- 

-         // build the days

-         var agmt_days = "";

-         if ( $("#schedule-sun").is(":checked") ){

-           agmt_days =  "0";

-         }

-         if ( $("#schedule-mon").is(":checked") ){

-           agmt_days += "1";

-         }

-         if ( $("#schedule-tue").is(":checked") ){

-           agmt_days += "2";

-         }

-         if ( $("#schedule-wed").is(":checked") ){

-           agmt_days += "3";

-         }

-         if ( $("#schedule-thu").is(":checked") ){

-           agmt_days += "4";

-         }

-         if ( $("#schedule-fri").is(":checked") ){

-           agmt_days += "5";

-         }

-         if ( $("#schedule-sat").is(":checked") ){

-           agmt_days += "6";

-         }

-         if (agmt_days == "" ){

-           popup_msg("Error", "You must set at least one day in the schedule to perform replication");

-           return;

-         }

-         // Set final value

-         agmt_schedule = agmt_start + "-" + agmt_end + " " + agmt_days;

- 

-         if (!editing ){

-           cmd_args.push('--schedule=' + agmt_schedule);

-         } else {

-           if ( !('nsds5replicaupdateschedule' in repl_agmt_values) ||

-                agmt_schedule != repl_agmt_values['nsds5replicaupdateschedule'] )

-           {

-             cmd_args.push('--schedule=' + agmt_schedule);

-           }

-         }

-       } else {

-         // if "sync all the time" is checked, might need to remove the schedule attribute

-         if ('nsds5replicaupdateschedule' in repl_agmt_values) {

-           cmd_args.push('--schedule=');

-         }

-       }

-       if (agmt_init == "online-init") {

-         init_replica = true;

-       }

- 

-       // Create agreement in DS

-       if ( editing ) {

-         cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'set', '--suffix=' + suffix ];

-       } else {

-         cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'create', '--suffix=' + suffix];

-       }

-       cmd = cmd.concat(cmd_args);

-       log_cmd('#agmt-save (click)', 'Create/Set replication agreement', cmd);

-       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-         if (editing){

-           popup_success('Successfully edited replication agreement');

-         } else {

-           popup_success('Successfully created replication agreement');

-         }

-         if (init_replica) {

-           // Launch popup stating initialization has begun

-           var init_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'init', '--suffix=' + suffix, agmt_name ];

-           log_cmd('#agmt-save (click)', 'Initialize agreement', init_cmd);

-           cockpit.spawn(init_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function() {

-             get_and_set_repl_agmts();

-           }).fail(function(data) {

-             popup_err("Failed to initialize replication agreement", data.message);

-           });

-         } else {

-           get_and_set_repl_agmts();

-         }

-       }).fail(function(data) {

-         if (editing) {

-          popup_err("Failed to edit replication agreement", data.message);

-        } else {

-          popup_err("Failed to create replication agreement", data.message);

-        }

-       });

- 

-       // Done, close the form

-       $("#agmt-form").modal('toggle');

-       clear_agmt_wizard();

-     });

- 

-     /*

-      * Initialize agreement

-      */

-     $(document).on('click', '.agmt-init-btn', function(e) {

-       e.preventDefault();

-       var suffix = $("#select-repl-agmt-suffix").val();

-       var data = repl_agmt_table.row( $(this).parents('tr') ).data();

-       var agmt_name = data[0];

-       var row_idx = $(this).closest('tr').index();

-       repl_agmt_table.cell({row: row_idx, column: 5}).data(progress_html).draw();

- 

-       var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'init', '--suffix=' + suffix, '"' + agmt_name + '"' ];

-       log_cmd('.agmt-init-btn (click)', 'Initialize replication agreement', cmd);

-       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-         var init_idx = agmt_init_counter;

-         agmt_init_counter += 1;

-         agmt_init_intervals[init_idx] = setInterval( do_agmt_init, 2000, suffix, agmt_name, init_idx);

-       }).fail(function(data) {

-         get_and_set_repl_agmts();

-         popup_err("Failed to initialize agreement", data.message);

-       });

-     });

- 

-     $(document).on('click', '.winsync-agmt-init-btn', function(e) {

-       e.preventDefault();

-       var suffix = $("#select-repl-winsync-suffix").val();

-       var data = repl_winsync_agmt_table.row( $(this).parents('tr') ).data();

-       var agmt_name = data[0];

-       var row_idx = $(this).closest('tr').index();

-       repl_winsync_agmt_table.cell({row: row_idx, column: 5}).data(progress_html).draw();

- 

-       var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'init', '--suffix=' + suffix, '"' + agmt_name + '"' ];

-       log_cmd('.winsync-agmt-init-btn (click)', 'Initialize replication winsync agreement', cmd);

-       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-         var init_idx = agmt_init_counter;

-         agmt_init_counter += 1;

-         agmt_init_intervals[init_idx] = setInterval( do_winsync_agmt_init, 2000, suffix, agmt_name, init_idx);

-       }).fail(function(data) {

-         get_and_set_repl_winsync_agmts();

-         popup_err("Failed to initialize winsync agreement", data.message);

-       });

-     });

- 

- 

-     /* Store the repl dn from the table when opening the mgr delete confirmation modal */

-     $(document).on('click', '.remove-repl-mgr', function(e) {

-       e.preventDefault();

-       var mgr_row =  $(this).parent().parent();

-       var suffix = $("#select-repl-cfg-suffix").val();

-       var mgr_dn = mgr_row.children("td:nth-child(1)");

-       popup_confirm("Are you sure you want to delete Replication Manager:  <b>" + mgr_dn.text() + "<b>", "Confirmation", function (yes) {

-         if (yes) {

-           var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication' ,'set',

-                      '--repl-del-bind-dn=' + mgr_dn.text(), '--suffix=' + suffix];

-           log_cmd('.remove-repl-mgr (click)', 'Remove replication manager ', cmd);

-           cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-             /* Remove the manager entry */

-             var del_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication',

-                            'delete-manager', "--name=" + mgr_dn.text()];

-             log_cmd('.remove-repl-mgr(click)', 'Delete replication manager entry', del_cmd);

-             cockpit.spawn(del_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-               popup_success('Successfully removed replication manager');

-               get_and_set_repl_config();

-             });

-           }).fail(function(data) {

-             get_and_set_repl_config();

-             popup_err("Failed to remove replication manager", data.message);

-           });

-         }

-       });

-     });

- 

-     /*

-      * Delete repl agreement

-      */

-     $(document).on('click', '.agmt-del-btn', function(e) {

-       e.preventDefault();

-       var data = repl_agmt_table.row( $(this).parents('tr') ).data();

-       var del_agmt_name = data[0];

-       var agmt_row = $(this);

-       popup_confirm("Are you sure you want to delete replication agreement: <b>" + del_agmt_name + "</b>", "Confirmation", function (yes) {

-         if (yes) {

-           var suffix = $("#select-repl-agmt-suffix").val();

-           var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'delete', '--suffix=' + suffix, '"' + del_agmt_name + '"'];

-           log_cmd('.agmt-del-btn (click)', 'Delete replication agreement', cmd);

-           cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-             popup_success('Successfully removed replication agreement');

-             // Update table

-             repl_agmt_table.row( agmt_row.parents('tr') ).remove().draw( false );

-           }).fail(function(data) {

-             get_and_set_repl_config();

-             popup_err("Failed to remove replication agreement", data.message);

-           });

-         }

-       });

-     });

- 

-     $(document).on('click', '.winsync-agmt-del-btn', function(e) {

-       e.preventDefault();

-       var data = repl_winsync_agmt_table.row( $(this).parents('tr') ).data();

-       var del_agmt_name = data[0];

-       var agmt_row = $(this);

-       popup_confirm("Are you sure you want to delete replication agreement: <b>" + del_agmt_name + "</b>", "Confirmation", function (yes) {

-         if (yes) {

-           var suffix = $("#select-repl-agmt-suffix").val();

-           var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'delete', '--suffix=' + suffix, '"' + del_agmt_name + '"'];

-           log_cmd('.winsync-agmt-del-btn (click)', 'Delete replication winsync agreement', cmd);

-           cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-             popup_success('Successfully removed replication winsync agreement');

-             // Update table

-             repl_winsync_agmt_table.row( agmt_row.parents('tr') ).remove().draw( false );

-           }).fail(function(data) {

-             get_and_set_repl_config();

-             popup_err("Failed to remove replication winsync agreement", data.message);

-           });

-         }

-       });

-     });

- 

-     /*

-      * Edit Agreement

-      */

-     $(document).on('click', '.agmt-edit-btn', function(e) {

-       e.preventDefault();

-       clear_agmt_wizard();

-       var suffix = $("#select-repl-agmt-suffix").val();

-       var data = repl_agmt_table.row( $(this).parents('tr') ).data();

-       var edit_agmt_name = data[0];

-       // Set agreement form values

-       $("#agmt-wizard-title").html("<b>Edit Replication Agreement</b>");

- 

-       // Hide init dropdown

-       $("#init-agmt-dropdown").hide();

- 

-       // Get agreement from DS and populate form

-       var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'get', '--suffix=' + suffix, '"' + edit_agmt_name + '"'];

-       log_cmd('.agmt-edit-btn (click)', 'Edit replication agreement', cmd);

-       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-         var agmt_obj = JSON.parse(data);

-         var frac_attrs = "";

-         var frac_tot_attrs = "";

-         var strip_attrs = "";

- 

-         $("#agmt-cn").val(edit_agmt_name);

-         for (var attr in agmt_obj['attrs']) {

-           var val = agmt_obj['attrs'][attr][0];

-           attr = attr.toLowerCase();

-           $("#" + attr).val(val);

-           repl_agmt_values[attr] = val;

-         }

- 

-         // Fill Password Confirm Input

-         if ( 'nsds5replicacredentials' in agmt_obj['attrs'] ){

-           $("#nsds5replicacredentials-confirm").val(agmt_obj['attrs']["nsds5replicacredentials"][0]);

-         }

- 

-         // Transport info

-         val = agmt_obj['attrs']["nsds5replicatransportinfo"][0].toLowerCase();

-         if (val == "ldap"){

-           $("#nsds5replicatransportinfo").val("LDAP");

-         } else if (val == "ldaps"){

-           $("#nsds5replicatransportinfo").val("LDAPS");

-         } else if (val == "starttls" || val == "tls"){

-           $("#nsds5replicatransportinfo").val("StartTLS");

-         }

- 

-         // Bind Method

-         val = agmt_obj['attrs']["nsds5replicabindmethod"][0].toLowerCase();

-         if (val == "simple"){

-           $("#nsds5replicabindmethod").val("SIMPLE");

-         } else if (val == "sasl/digest-md5"){

-           $("#nsds5replicabindmethod").val("SASL/DIGEST-MD5");

-         } else if (val == "sasl/gssapi"){

-           $("#nsds5replicabindmethod").val("SASL/GSSAPI");

-         } else if (val == "sslclientauth"){

-           $("#nsds5replicatransportinfo").val("SSLCLIENTAUTH");

-         }

- 

-         // Load fractional lists

-         if ( 'nsds5replicatedattributelist' in agmt_obj['attrs'] ){

-           frac_attrs = agmt_obj['attrs']['nsds5replicatedattributelist'][0];

-           frac_attrs = frac_attrs.replace(frac_prefix, "").split(" ");

-           for(var i = 0; i < frac_attrs.length; i++) {

-             var opt = frac_attrs[i];

-             if (opt != "") {

-               var option = $('<option></option>').attr("value", opt).text(opt);

-               $("#frac-exclude-list").append(option);

-             }

-           }

-         }

-         if ( 'nsds5replicatedattributelisttotal' in agmt_obj['attrs'] ){

-           frac_tot_attrs = agmt_obj['attrs']['nsds5replicatedattributelisttotal'][0];

-           frac_tot_attrs = frac_tot_attrs.replace(frac_prefix, "").split(" ");

-           for(var i = 0; i < frac_tot_attrs.length; i++) {

-             var opt = frac_tot_attrs[i];

-             if (opt != "") {

-              var option = $('<option></option>').attr("value", opt).text(opt);

-               $("#frac-exclude-tot-list").append(option);

-             }

-           }

-         }

-         if ( 'nsds5replicastripattrs' in agmt_obj['attrs'] ){

-           strip_attrs = agmt_obj['attrs']['nsds5replicastripattrs'][0];

-           strip_attrs = strip_attrs.split(" ");

-           for(var i = 0; i < strip_attrs.length; i++) {

-             var opt = strip_attrs[i];

-             if (opt != "") {

-               var option = $('<option></option>').attr("value", opt).text(opt);

-               $("#frac-strip-list").append(option);

-             }

-           }

-         }

- 

-         // Set schedule

-         if ( 'nsds5replicaupdateschedule' in agmt_obj['attrs'] ){

-           var val =  agmt_obj['attrs']['nsds5replicaupdateschedule'][0];

-           var parts = val.split(" ");

-           var days = parts[1];

-           var times = parts[0].split("-");

-           var start_time = times[0].substring(0,2) + ":" + times[0].substring(2,4);

-           var end_time = times[1].substring(0,2) + ":" + times[1].substring(2,4);

- 

-           $("#agmt-schedule-checkbox").prop('checked', false);

-           $('#agmt-schedule-panel *').attr('disabled', false);

-           $("#schedule-settings").show();

- 

-           $("#agmt-start-time").val(start_time);

-           $("#agmt-end-time").val(end_time);

-           if ( days.indexOf('0') != -1){ // Sunday

-             $("#schedule-sun").prop('checked', true);

-           }

-           if ( days.indexOf('1') != -1){ // Monday

-             $("#schedule-mon").prop('checked', true);

-           }

-           if ( days.indexOf('2') != -1){ // Tuesday

-             $("#schedule-tue").prop('checked', true);

-           }

-           if ( days.indexOf('3') != -1){ // Wednesday

-             $("#schedule-wed").prop('checked', true);

-           }

-           if ( days.indexOf('4') != -1){ // Thursday

-             $("#schedule-thu").prop('checked', true);

-           }

-           if ( days.indexOf('5') != -1){ // Friday

-             $("#schedule-fri").prop('checked', true);

-           }

-           if ( days.indexOf('6') != -1){ // Saturday

-             $("#schedule-sat").prop('checked', true);

-           }

-         }

-         // Finally Open form

-         $("#agmt-form").modal('toggle');

-       }).fail(function(data) {

-         popup_err("Failed to get replication agreement entry", data.message);

-       });

-     });

- 

-     /*

-      * Edit Winsync Agreement

-      */

-     $(document).on('click', '.winsync-agmt-edit-btn', function(e) {

-       e.preventDefault();

-       clear_winsync_agmt_wizard();

-       var suffix = $("#select-repl-winsync-suffix").val();

-       var data = repl_winsync_agmt_table.row( $(this).parents('tr') ).data();

-       var edit_agmt_name = data[0];

- 

-       // Get agreement from DS and populate form

-       var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'get', '--suffix=' + suffix, '"' + edit_agmt_name + '"'];

-       log_cmd('.winsync-agmt-edit-btn (click)', 'Get replication winsync agreement', cmd);

-       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-         var agmt_obj = JSON.parse(data);

- 

-         // Set agreement form values

-         $("#winsync-agmt-wizard-title").html("<b>Edit Winsync Agreement</b>");

-         // Hide init dropdown

-         $("#winsync-init-chbx").hide();

-         $("#winsync-agmt-cn").val(edit_agmt_name);

-         for (var attr in agmt_obj['attrs']) {

-           var val = agmt_obj['attrs'][attr][0];

-           attr = attr.toLowerCase();

-           $("#winsync-" + attr).val(val);

-           if (val == "on") {

-             $("#winsync-" + attr + "-checkbox").prop('checked', true);

-           } else if (val == "off") {

-             $("#winsync-" + attr + "-checkbox").prop('checked', false);

-           }

-           repl_winsync_agmt_values[attr] = val;

-         }

- 

-         // Fill Password Confirm Input

-         if ( 'nsds5replicacredentials' in agmt_obj['attrs'] ){

-           $("#winsync-nsds5replicacredentials-confirm").val(agmt_obj['attrs']["nsds5replicacredentials"][0]);

-         }

- 

-         // Transport info

-         val = agmt_obj['attrs']["nsds5replicatransportinfo"][0].toLowerCase();

-         if (val == "ldap"){

-           $("#winsync-nsds5replicatransportinfo").val("LDAP");

-         } else if (val == "ldaps"){

-           $("#winsync-nsds5replicatransportinfo").val("LDAPS");

-         } else if (val == "starttls" || val == "tls"){

-           $("#winsync-nsds5replicatransportinfo").val("StartTLS");

-         }

- 

-         // Finally open the form

-         $("#winsync-agmt-form").modal('toggle');

-       }).fail(function(data) {

-         popup_err("Failed to load replication winsync agreement entry", data.message);

-       });

-     });

- 

- 

-     // Handle disabling/enabling of agmt schedule panel

-     $('#agmt-schedule-panel *').attr('disabled', true); /// Disabled by default

-     $("#agmt-schedule-checkbox").change(function() {

-       if(this.checked) {

-         $('#agmt-schedule-panel *').attr('disabled', true);

-         $("#schedule-settings").hide();

-       } else {

-         $('#agmt-schedule-panel *').attr('disabled', false);

-         $("#schedule-settings").show();

-       }

-     });

- 

-     // Based on the connection type change the agmt-auth options

-     $("#nsds5replicatransportinfo").change(function() {

-       var ldap_opts = {"Simple": "Simple",

-                        "SASL/DIGEST-MD5": "SASL/DIGEST-MD5",

-                        "SASL/GSSAPI": "SASL/GSSAPI"};

-       var ldaps_opts = {"Simple": "Simple",

-                         "SSL Client Authentication": "SSL Client Authentication",

-                         "SASL/DIGEST-MD5": "SASL/DIGEST-MD5"};

-       var $auth = $("#nsds5replicabindmethod");

-       $auth.empty();

-       var conn = $('#nsds5replicatransportinfo').val();

-       if (conn == "LDAP"){

-         $.each(ldap_opts, function(key, value) {

-           $auth.append($("<option></option>").attr("value", value).text(key));

-         });

-       } else {

-         // TLS options

-         $.each(ldaps_opts, function(key, value) {

-           $auth.append($("<option></option>").attr("value", value).text(key));

-         });

-       }

-       $("#nsds5replicabinddn").prop('disabled', false);

-       $("#nsds5replicacredentials").prop('disabled', false);

-       $("#nsds5replicacredentials-confirm").prop('disabled', false);

-     });

- 

-     // Check for auth changes and disable/enable bind DN & password

-     $("#nsds5replicabindmethod").change(function() {

-       var authtype = $('#nsds5replicabindmethod').val();

-       if (authtype == "SSL Client Authentication") {

-         $("#nsds5replicabinddn").prop('disabled', true);

-         $("#nsds5replicacredentials").prop('disabled', true);

-         $("#nsds5replicacredentials-confirm").prop('disabled', true);

-       } else {

-         $("#nsds5replicabinddn").prop('disabled', false);

-         $("#nsds5replicacredentials").prop('disabled', false);

-         $("#nsds5replicacredentials-confirm").prop('disabled', false);

-       }

-     });

- 

-     // Create time picker for agmt schedule (start end times))

-     $('input.timepicker').timepicker({

-       'timeFormat': 'H:i',

-       'step': 15

-     });

- 

-     // Accordion opening/closings

- 

-     $(".ds-accordion-panel").css('display','none');

- 

-     $("#repl-config-accordion").on("click", function() {

-       this.classList.toggle("active");

-       var panel = this.nextElementSibling;

-       if (panel.style.display === "block") {

-         var show = "&#9658 Show Advanced Settings ";

-         $(this).html(show);

-         panel.style.display = "none";

-         $(this).blur();

-       } else {

-         var hide = "&#9660 Hide Advanced Settings ";

-         $(this).html(hide);

-         panel.style.display = "block";

-         $(this).blur();

-       }

-     });

- 

-     $("#frac-accordion").on("click", function() {

-       this.classList.toggle("active");

-       var panel = this.nextElementSibling;

-       if (panel.style.display === "block") {

-         var show = "&#9658 Show Fractional Settings ";

-         $(this).html(show);

-         panel.style.display = "none";

-         $(this).blur();

-       } else {

-         var hide = "&#9660 Hide Fractional Settings ";

-         $(this).html(hide);

-         panel.style.display = "block";

-         $(this).blur();

-       }

-     });

- 

-     $("#schedule-accordion").on("click", function() {

-       this.classList.toggle("active");

-       var panel = this.nextElementSibling;

-       if (panel.style.display === "block") {

-         var show = "&#9658 Show Schedule Settings ";

-         $(this).html(show);

-         panel.style.display = "none";

-         $(this).blur();

-       } else {

-         var hide = "&#9660 Hide Schedule Settings ";

-         $(this).html(hide);

-         panel.style.display = "block";

-         $(this).blur();

-       }

-     });

- 

-     /*

-      * Handle the repl agmt wizard select lists

-      */

- 

-     /*

-      * Set the "select"'s list-id in a hidden field on the select attribute form

-      * so we know what list to update after the selection

-      */

- 

-     $(".ds-fractional-btn").on('click', function() {

-       // reset the list

-       $("#select-attr-list").prop('selectedIndex',-1);

-     });

-     $("#frac-list-add-btn").on('click', function () {

-       $("#attr-form-id").val("frac-exclude-list");

-     });

-     $("#frac-total-list-add-btn").on('click', function () {

-       $("#attr-form-id").val("frac-exclude-tot-list");

-     });

-     $("#frac-strip-list-add-btn").on('click', function () {

-       $("#attr-form-id").val("frac-strip-list");

-     });

- 

-     // Handle the attribute removal from the lists

-     $("#frac-list-remove-btn").on("click", function () {

-       $("#frac-exclude-list").find('option:selected').remove();

-     });

-     $("#frac-total-list-remove-btn").on("click", function () {

-       $("#frac-exclude-tot-list").find('option:selected').remove();

-     });

-     $("#frac-strip-list-remove-btn").on("click", function () {

-       $("#frac-strip-list").find('option:selected').remove();

-     });

- 

-     // Update agmt form attribute selection lists

-     $("#select-attr-save").on("click", function () {

-       // Get the id from the hidden input filed and append the attribute to it

-       var list_id = $("#attr-form-id").val();

-       var add_attrs = $("#select-attr-list").find('option:selected');

-       if (add_attrs && add_attrs != '' && add_attrs.length > 0) {

-         for (var i = 0; i < add_attrs.length; i++) {

-           if ( $('#' + list_id + ' option[value="' + add_attrs[i].text + '"]').val() === undefined) {

-             $('#' + list_id).append($("<option/>").val(add_attrs[i].text).text(add_attrs[i].text));

-           }

-         }

-         sort_list( $("#" + list_id) );

-       }

-       $("#select-attr-form").modal('toggle');

-     });

- 

- 

-     /*

-      * Modals

-      */

- 

-     // Winsync-agmt Agreement Wizard

- 

-     $("#winsync-create-agmt").on("click", function() {

-       clear_winsync_agmt_wizard(); // TODO

-     });

- 

-     $("#create-agmt").on("click", function() {

-       clear_agmt_wizard();

-     });

- 

-     $("#winsync-agmt-save").on("click", function() {

-       var suffix = $("#select-repl-winsync-suffix").val();

-       var cmd = [];

-       var cmd_args = [];

-       var param_err = false;

-       var editing = false;

-       var init_replica = false;

- 

-       // Check passwords match:

-       var agmt_passwd = $("#winsync-nsds5replicacredentials").val();

-       var passwd_confirm = $("#winsync-nsds5replicacredentials-confirm").val();

-       if (agmt_passwd != passwd_confirm) {

-         popup_msg("Attention!", "Passwords do not match!");

-         return;

-       }

-       // Get form values

-       var repl_root = $("#select-repl-winsync-suffix").val();

-       var agmt_name = $("#winsync-agmt-cn").val();

-       var win_domain = $("#winsync-nsds7windowsdomain").val();

-       var agmt_host = $("#winsync-nsds5replicahost").val();

-       var agmt_port = $("#winsync-nsds5replicaport").val();

-       var win_subtree = $("#winsync-nsds7windowsreplicasubtree").val();

-       var ds_subtree = $("#winsync-nsds7directoryreplicasubtree").val();

-       var bind_dn = $("#winsync-nsds5replicabinddn").val();

-       var bind_pw = $("#winsync-nsds5replicacredentials").val();

-       var agmt_conn = $("#winsync-nsds5replicatransportinfo").val();

-       var sync_new_users = "off";

-       var sync_new_groups = "off";

-       if ( $("#winsync-nsds7newwinusersyncenabled-checkbox").is(":checked") ){

-         sync_new_users = "on";

-       }

-       if ( $("#winsync-nsds7newwingroupsyncenabled-checkbox").is(":checked") ){

-         sync_new_groups = "on"

-       }

-       if ($("#winsync-agmt-wizard-title").text().includes('Edit') ) {

-         editing = true;

-       }

- 

-       // Check required settings

-       if (bind_pw == "") {

-         $("#winsync-nsds5replicacredentials").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#winsync-nsds5replicacredentials").css("border-color", "initial");

-         cmd_args.push("--bind-passwd=" + bind_pw);

-       }

-       if ( ds_subtree == "") {

-         $("#winsync-nsds7directoryreplicasubtree").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#winsync-nsds7directoryreplicasubtree").css("border-color", "initial");

-         cmd_args.push("--ds-subtree=" + ds_subtree);

-       }

-       if ( win_subtree == "") {

-         $("#winsync-nsds7windowsreplicasubtree").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#winsync-nsds7windowsreplicasubtree").css("border-color", "initial");

-         cmd_args.push("--win-subtree=" + win_subtree);

-       }

-       if ( win_domain == "") {

-         $("#winsync-nsds7windowsdomain").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#winsync-nsds7windowsdomain").css("border-color", "initial");

-         cmd_args.push("--win-domain=" + win_domain);

-       }

-       if ( agmt_port == "") {

-         $("#winsync-nsds5replicaport").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#winsync-nsds5replicaport").css("border-color", "initial");

-         cmd_args.push("--port=" + agmt_port);

-       }

-       if ( agmt_host == "") {

-         $("#winsync-nsds5replicahost").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#winsync-nsds5replicahost").css("border-color", "initial");

-         cmd_args.push('--host=' + agmt_host);

-       }

-       if ( agmt_conn == "") {

-         $("#winsync-nsds5replicatransportinfo").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#winsync-nsds5replicatransportinfo").css("border-color", "initial");

-         cmd_args.push('--conn-protocol=' + agmt_conn);

-       }

-       if ( bind_dn == "") {

-         $("#winsync-nsds5replicabinddn").css("border-color", "red");

-         param_err = true;

-       } else {

-         $("#winsync-nsds5replicabinddn").css("border-color", "initial");

-         cmd_args.push('--bind-dn=' + bind_dn);

-       }

-       if (param_err ){

-         popup_msg("Error", "Missing required parameters");

-         return;

-       }

- 

-       // Checkboxes

-       if ( ($("#winsync-nsds7newwinusersyncenabled-checkbox").is(":checked")) ){

-         sync_new_users = "on";

-       }

-       if ( ($("#winsync-nsds7newwingroupsyncenabled-checkbox").is(":checked")) ){

-         sync_groups_users = "on";

-       }

-       if ( ($("#winsync-init-chbx").is(":checked")) ){

-         init_replica = true;

-       }

-       cmd_args.push('--sync-users=' + sync_new_users);

-       cmd_args.push('--sync-groups=' + sync_new_groups);

- 

-       // Create winsync agreement in DS

-       if ( editing ) {

-         cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'set', '"' + agmt_name + '"', '--suffix=' + suffix ];

-       } else {

-         cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'create', '"' + agmt_name + '"', '--suffix=' + suffix];

-       }

-       cmd = cmd.concat(cmd_args);

-       log_cmd('#winsync-agmt-save (click)', 'Create/Set replication winsync agreement', cmd);

-       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-         if (editing){

-           popup_success('Successfully edited replication winsync agreement');

-         } else {

-           popup_success('Successfully created replication winsync agreement');

-         }

-         if (init_replica) {

-           // Launch popup stating initialization has begun

-           var init_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'init', '--suffix=' + suffix, '"' + agmt_name + '"' ];

-           log_cmd('#winsync-agmt-save (click)', 'Initialize winsync agreement', init_cmd);

-           cockpit.spawn(init_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function() {

-             popup_msg("Agreement Initialization", "The agreement initialization has begun...");

-           }).fail(function(data) {

-             popup_err("Failed to initialize replication agreement", data.message);

-           });

-         }

-         // Reload table

-         get_and_set_repl_winsync_agmts();

-       }).fail(function(data) {

-         if (editing) {

-          popup_err("Failed to edit replication winsync agreement", data.message);

-        } else {

-          popup_err("Failed to create replication winsync agreement", data.message);

-        }

-       });

- 

-       // Reload winsync agmt table

-       $("#winsync-agmt-form").modal('toggle');

-     });

- 

-     // Create CleanAllRUV Task - TODO

-     $("#create-cleanallruv-btn").on("click", function() {

-       clear_cleanallruv_form();

-     });

- 

-     $("#cleanallruv-save").on("click", function() {

-       // Do the actual save in DS

-       var suffix = $("#cleanallruv-suffix").val();

-       var rid = $("#cleanallruv-rid").val();

-       var force = false;

-       if ( $("#force-clean").is(":checked") ) {

-         force = true;

-       }

-       if (suffix == ""){

-         popup_msg("Error", "There is no suffix to run the task on");

-         return;

-       }

-       if (rid == ""){

-         popup_msg("Error", "You must enter a Replica ID to clean");

-         return;

-       }

-       $("#cleanallruv-form").modal('toggle');

-       var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-tasks', 'cleanallruv', '--suffix=' + suffix, '--replica-id=' + rid ];

-       if (force) {

-         cmd.push('--force-cleaning');

-       }

-       log_cmd('#cleanallruv-save (click)', 'Create CleanAllRUV Task', cmd);

-       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function() {

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

-           var obj = JSON.parse(data);

-           for (var idx in obj['items']) {

-             task_attrs = obj['items'][idx]['attrs'];

-             var task_create_date = task_attrs['createtimestamp'][0];

-             var abort_btn = cleanallruv_action_html;

-             if (task_attrs['nstaskstatus'][0].includes('Successfully cleaned rid') ){

-               abort_btn = "";

-             }

-             repl_clean_table.row.add( [

-               task_attrs['cn'][0],

-               get_date_string(task_create_date),

-               suffix,

-               task_attrs['replica-id'][0],

-               task_attrs['nstaskstatus'][0],

-               abort_btn

-             ] ).draw( false );

-           }

-         }).fail( function (data) {

-           popup_err("Failed to get CleanAllRUV Tasks", data.message);

-         });

-       }).fail( function (data) {

-         popup_err("Failed to create CleanAllRUV Task", data.message);

-       });

-     });

- 

-     $(document).on('click', '.abort_cleanallruv_btn', function(e) {

-       e.preventDefault();

-       var data = repl_clean_table.row( $(this).parents('tr') ).data();

-       var suffix = $("#cleanallruv-suffix").val();

-       var task_rid = data[3];

-       popup_confirm("Are you sure you want to abort the cleaning task on: <b>" + suffix + "</b> for Replica ID <b>" + task_rid + "</b>", "Confirmation", function (yes) {

-         if (yes) {

-           var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-tasks', 'abort-cleanallruv', '--replica-id=' + task_rid, '--suffix=' + suffix];

-           log_cmd('.abort_cleanallruv_btn (click)', 'Abort CleanAllRUV task', cmd);

-           cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

-             popup_success("Creating task to abort the CleanAllRUV Task");

-           }).fail(function(data) {

-             popup_err("Failed to add Abort CleanAllRUV Task", data.message);

-           });

-         }

-       });

-     });

- 

-     $("#refresh-cleanlist-btn").on('click', function () {

-       // Refresh the list

-       get_and_set_cleanallruv();

-     });

-     $("#refresh-agmts-btn").on('click', function () {

-       // Refresh the list

-       get_and_set_repl_agmts();

-     });

-     $("#refresh-winsync-agmts-btn").on('click', function () {

-       // Refresh the list

-       get_and_set_repl_winsync_agmts();

-     });

- 

-     /*

-      * Add repl manager modal

-      */

-     $("#add-repl-manager").on("click", function() {

-       clear_repl_mgr_form();

-     });

- 

-     $("#add-repl-mgr-save").on("click", function() {

-       var suffix = $("#select-repl-cfg-suffix").val();

-       var repl_dn = $("#add-repl-mgr-dn").val();

-       var repl_pw = $("#add-repl-pw").val();

-       var repl_pw_confirm = $("#add-repl-pw-confirm").val();

- 

-       // Validate

-       if (repl_dn == ""){

-         popup_msg("Attention!", "Replication Manager DN is required");

-         return;

-       }

-       if (!valid_dn(repl_dn)){

-         popup_msg("Attention!", "Invalid DN for Replication Manager");

-         return;

-       }

-       if (repl_pw == ""){

-         popup_msg("Attention!", "Replication Manager DN password is required");

-         return;

-       }

-       if (repl_pw != repl_pw_confirm) {

-         popup_msg("Attention!", "Passwords do not match");

-         $("#add-repl-pw").val("");

-         $("#add-repl-pw-confirm").val("");

-         return;

-       }

- 

-       // Add manager

-       var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','replication', 'create-manager', '--name=' + repl_dn, '--passwd=' + repl_pw, '--suffix=' + suffix ];

-       log_cmd('#add-repl-mgr-save (click)', 'Create replication manager entry and add it to config', cmd);

-       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function() {

-         get_and_set_repl_config();

-         popup_success("Success created replication manager and added it to the replication configuration");

-         $("#add-repl-mgr-form").modal('toggle');

-       }).fail( function(err) {

-         popup_err("Failed to create replication manager entry", err.message);

-         $("#add-repl-mgr-form").modal('toggle');

-       });

-     });

- 

-     /* Send update now */

-     $(document).on('click', '.agmt-send-updates-btn', function(e) {

-       var suffix = $("#select-repl-agmt-suffix").val();

-       var data = repl_agmt_table.row( $(this).parents('tr') ).data();

-       var update_agmt_name = data[0];

- 

-       var update_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'poke', update_agmt_name, '--suffix=' + suffix];

-       log_cmd('.agmt-send-updates-btn (click)', 'Trigger send updates now (replication)', update_cmd);

-       cockpit.spawn(update_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function() {

-         popup_success("Triggered replication updates");

-       }).fail( function(err) {

-         popup_err("Failed to send updates", err.message);

-       });

-     });

- 

-     /* Send update now (winsync) */

-     $(document).on('click', '.winsync-agmt-send-updates-btn', function(e) {

-       var suffix = $("#select-repl-winsync-suffix").val();

-       var data = repl_winsync_agmt_table.row( $(this).parents('tr') ).data();

-       var update_agmt_name = data[0];

- 

-       var update_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'poke', update_agmt_name, '--suffix=' + suffix];

-       log_cmd('.winsync-agmt-send-updates-btn (click)', 'Trigger send updates now (winsync)', update_cmd);

-       cockpit.spawn(update_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function() {

-         popup_success("Kick started replication");

-       }).fail( function(err) {

-         popup_err("Failed to send updates", err.message);

-       });

-     });

- 

-     /*

-      * Enable/Disable repl agmt

-      */

-     $(document).on('click', '.agmt-enable-btn', function(e) {

-       var suffix = $("#select-repl-agmt-suffix").val();

-       var data = repl_agmt_table.row( $(this).parents('tr') ).data();

-       var enable_agmt_name = data[0];

-       var agmt_state = data[3];  // 4th column in table

-       if (agmt_state.toLowerCase() == "enabled") {

-         // We must be trying to disable this agreement - confirm it

-         popup_confirm("Are you sure you want to disable replication agreement: <b>" + enable_agmt_name + "</b>", "Confirmation", function (yes) {

-           if (yes) {

-             var disable_cmd =  [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'disable', enable_agmt_name, '--suffix=' + suffix];

-             log_cmd('.agmt-enable-btn (click)', 'Disable replication agreement', disable_cmd);

-             cockpit.spawn(disable_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function() {

-               get_and_set_repl_agmts();

-               popup_success("The replication agreement has been disabled.");

-             }).fail( function(err) {

-               popup_err("Failed to disable agreement", err.message);

-             });

-           }

-         });

-       } else {

-         // Enabling agreement - confirm it

-         popup_confirm("Are you sure you want to enable replication agreement: <b>" + enable_agmt_name + "</b>", "Confirmation", function (yes) {

-           if (yes) {

-             var enable_cmd =  [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'enable', enable_agmt_name, '--suffix=' + suffix];

-             log_cmd('.agmt-enable-btn (click)', 'Enable replication agreement', enable_cmd);

-             cockpit.spawn(enable_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function() {

-               get_and_set_repl_agmts();

-               popup_success("The replication agreement has been enabled.");

-             }).fail( function(err) {

-               popup_err("Failed to enable agreement", err.message);

-             });

-           }

-         });

-       }

-     });

- 

-     /*

-      * Enable/Disable winsync repl agmt

-      */

-     $(document).on('click', '.winsync-agmt-enable-btn', function(e) {

-       var suffix = $("#select-repl-winsync-suffix").val();

-       var data = repl_winsync_agmt_table.row( $(this).parents('tr') ).data();

-       var enable_agmt_name = data[0];

-       var agmt_state = data[3];  // 4th column in table

-       if (agmt_state.toLowerCase() == "enabled") {

-         // We must be trying to disable this agreement - confirm it

-         popup_confirm("Are you sure you want to disable replication agreement: <b>" + enable_agmt_name + "</b>", "Confirmation", function (yes) {

-           if (yes) {

-             var disable_cmd =  [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'disable', enable_agmt_name, '--suffix=' + suffix];

-             log_cmd('.winsync-agmt-enable-btn (click)', 'Disable winsync agreement', disable_cmd);

-             cockpit.spawn(disable_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function() {

-               get_and_set_repl_winsync_agmts();

-               popup_success("The replication agreement has been disabled.");

-             }).fail( function(err) {

-               popup_err("Failed to disable agreement", err.message);

-             });

-           }

-         });

-       } else {

-         // Enabling agreement - confirm it

-         popup_confirm("Are you sure you want to enable replication agreement: <b>" + enable_agmt_name + "</b>", "Confirmation", function (yes) {

-           if (yes) {

-             var enable_cmd =  [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-winsync-agmt', 'enable', enable_agmt_name, '--suffix=' + suffix];

-             log_cmd('.winsync-agmt-enable-btn (click)', 'Enable winsync agreement', disable_cmd);

-             cockpit.spawn(enable_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function() {

-               get_and_set_repl_winsync_agmts();

-               popup_success("The replication agreement has been enabled.");

-             }).fail( function(err) {

-               popup_err("Failed to enable agreement", err.message);

-             });

-           }

-         });

-       }

-     });

- 

-     $("#auth-mgr").click(function() {

-       $("#auth-group-div").hide();

-       $("#auth-manager-div").show();

-     });

-     $("#auth-group").click(function() {

-       $("#auth-manager-div").hide();

-       $("#auth-group-div").show();

-     });

- 

-     // Page is loaded, mark it as so...

-     repl_page_loaded = 1;

-   });

- });

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

                              </Col>

                          </Row>

                          <Row className="ds-margin-top" title="The name, or nickname, of the server certificate inthe NSS datgabase the server should use (nsSSLPersonalitySSL).">

-                             <Col className="ds-no-padding" sm={3}>

+                             <Col sm={3}>

                                  <ControlLabel>Server Certificate Name</ControlLabel>

                              </Col>

                              <Col sm={4}>

@@ -64,8 +64,8 @@ 

     </div>

  

     <button class="accordion ds-accordion" id="config-accordion" type="button">&#9658 Show Advanced Settings</button>

-    <div class="ds-accordion-panel">

-      <div class="ds-container">

+    <div class="ds-accordion-panel ds-indent">

+      <div class="ds-container ds-indent">

         <div class="ds-inline">

           <div>

             <input type="checkbox" class="ds-config-checkbox" id="nsslapd-schemacheck" checked><label

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

          "fonts",

          "images",

          "index.html",

-         "replication.html",

-         "replication.js",

          "schema.html",

          "schema.js",

          "servers.html",

@@ -61,8 +61,8 @@ 

          self.set('nsds5BeginReplicaRefresh', 'start')

  

      def check_reinit(self):

-         """Check the status of a reinit. Returns done and error. A correct

-         reinit will return (True, False).

+         """Check the status of a reinit. Returns done, inprogress, and error text. A correct

+         reinit will return (True, False, False).

  

          :returns: tuple(done, error), where done, error are bool.

          """
@@ -74,16 +74,16 @@ 

          if not status:

              pass

          elif 'replica busy' in status:

-             error = True

+             error = status

          elif 'Total update succeeded' in status:

              done = True

              inprogress = False

          elif 'Replication error' in status:

-             error = True

+             error = status

          elif 'Total update in progress' in status:

              inprogress = True

          elif 'LDAP error' in status:

-             error = True

+             error = status

  

          return (done, inprogress, error)

  

file modified
+1 -1
@@ -23,7 +23,7 @@ 

  # We need to be a factor to the backend monitor

  from lib389.monitor import MonitorBackend

  from lib389.index import Index, Indexes, VLVSearches, VLVSearch

- from lib389.tasks import ImportTask, ExportTask, Tasks

+ from lib389.tasks import ImportTask, ExportTask, CleanAllRUVTask, Tasks

  from lib389.encrypted_attributes import EncryptedAttr, EncryptedAttrs

  

  

@@ -115,7 +115,7 @@ 

          Delete chaining entry

          """

  

-         rdn = self.get_attr_val_utf8_l('cn')

+         rdn = self.get_attr_val_utf8_l('nsslapd-suffix')

          try:

              mt = self._mts.get(selector=rdn)

              mt.delete()

@@ -17,6 +17,7 @@ 

  from lib389.chaining import (ChainingLinks)

  from lib389.index import Index, VLVIndex, VLVSearches

  from lib389.monitor import MonitorLDBM

+ from lib389.replica import Replicas

  from lib389.utils import ensure_str, is_a_dn, is_dn_parent

  from lib389._constants import *

  from lib389.cli_base import (
@@ -187,7 +188,7 @@ 

          suffix_rdn_attr = args.suffix.split('=')[0].lower()

          if suffix_rdn_attr == 'dc':

              domain = create_base_domain(inst, args.suffix)

-             domain.add('aci', dc-aci)

+             domain.add('aci', dc_aci)

          elif suffix_rdn_attr == 'o':

              org = create_base_org(inst, args.suffix)

              org.add('aci', o_aci)
@@ -281,10 +282,19 @@ 

  def is_db_link(inst, rdn):

      links = ChainingLinks(inst).list()

      for link in links:

-         cn = ensure_str(link.get_attr_val('cn')).lower()

+         cn = link.get_attr_val_utf8('cn').lower()

          if cn == rdn.lower():

              return True

      return False

+     

+ 

+ def is_db_replicated(inst, suffix):

+     replicas = Replicas(inst)

+     try:

+         replica = replicas.get(suffix)

+         return True

+     except:

+         return False

  

  

  def backend_get_subsuffixes(inst, basedn, log, args):
@@ -343,8 +353,6 @@ 

      if link:

          icon = "glyphicon glyphicon-link"

          suffix_type = "dblink"

-     if replicated:

-         suffix_type = "replicated"

  

      return {

          "text": suffix,
@@ -352,6 +360,7 @@ 

          "selectable": True,

          "icon": icon,

          "type": suffix_type,

+         "replicated": replicated,

          "be": be_name,

          "nodes": []

      }
@@ -372,18 +381,20 @@ 

              if be_suffix == node_suffix.lower():

                  # We have our parent, now find the children

                  mts = be._mts.list()

+                 

                  for mt in mts:

-                     sub = mt.get_attr_val_utf8_l('nsslapd-parent-suffix')

+                     sub_parent = mt.get_attr_val_utf8_l('nsslapd-parent-suffix')

                      sub_be = mt.get_attr_val_utf8_l('nsslapd-backend')

-                     if sub == be_suffix:

+                     sub_suffix = mt.get_attr_val_utf8_l('cn')

+                     if sub_parent == be_suffix:

                          # We have a subsuffix (maybe a db link?)

-                         link = False

-                         if is_db_link(inst, sub_be):

-                             link = True

-                         node['nodes'].append(build_node(mt.get_attr_val_utf8_l('cn'),

+                         link = is_db_link(inst, sub_be)

+                         replicated = is_db_replicated(inst, sub_suffix)

+                         node['nodes'].append(build_node(sub_suffix,

                                                          sub_be,

                                                          subsuf=True,

-                                                         link=link))

+                                                         link=link,

+                                                         replicated=replicated))

  

                  # Recurse over the new subsuffixes

                  backend_build_tree(inst, be_insts, node['nodes'])
@@ -415,7 +426,8 @@ 

          sub = mt.get_attr_val_utf8_l('nsslapd-parent-suffix')

          if sub is not None:

              continue

-         nodes.append(build_node(suffix, be_name))

+         replicated = is_db_replicated(inst, suffix)

+         nodes.append(build_node(suffix, be_name, replicated=replicated))

  

      # No suffixes, return empty list

      if len(nodes) == 0:

@@ -71,7 +71,7 @@ 

      monitor_parser = subparsers.add_parser('monitor', help="Monitor the state of the instance")

      subcommands = monitor_parser.add_subparsers(help='action')

  

-     server_parser = subcommands.add_parser('server', help="Monitor the server statistics, connectinos and operations")

+     server_parser = subcommands.add_parser('server', help="Monitor the server statistics, connections and operations")

      server_parser.set_defaults(func=monitor)

  

      ldbm_parser = subcommands.add_parser('ldbm', help="Monitor the ldbm statistics, such as dbcache")
@@ -86,4 +86,4 @@ 

  

      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

+     chaining_parser.set_defaults(func=chaining_monitor)

@@ -104,6 +104,27 @@ 

  #

  # Replica config

  #

+ def get_ruv(inst, basedn, log, args):

+     replicas = Replicas(inst)

+     replica = replicas.get(args.suffix)

+     ruv = replica.get_ruv()

+     ruv_dict = ruv.format_ruv()

+     ruvs = ruv_dict['ruvs']

+     if args and args.json:

+         log.info(json.dumps({"type": "list", "items": ruvs}))

+     else:

+         add_gap = False

+         for ruv in ruvs:

+             if add_gap:

+                 log.info("")

+             log.info("RUV:        " + ruv['raw_ruv'])

+             log.info("Replica ID: " + ruv['rid'])

+             log.info("LDAP URL:   " + ruv['url'])

+             log.info("Min CSN:    " + ruv['csn'] + " (" + ruv['raw_csn'] + ")")

+             log.info("Max CSN:    " + ruv['maxcsn'] + " (" + ruv['raw_maxcsn'] + ")")

+             add_gap = True

+ 

+ 

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

      repl_root = args.suffix

      role = args.role.lower()
@@ -670,7 +691,7 @@ 

      elif inprogress:

          status = "Agreement initialization in progress."

      elif error:

-         status = "Agreement initialization failed."

+         status = "Agreement initialization failed: " + error

      if args.json:

          log.info(json.dumps(status))

      else:
@@ -893,7 +914,6 @@ 

      status = agmt.status(winsync=True, use_json=args.json)

      log.info(status)

  

- 

  #

  # Tasks

  #
@@ -1015,6 +1035,10 @@ 

      repl_disable_parser.set_defaults(func=disable_replication)

      repl_disable_parser.add_argument('--suffix', required=True, help='The DN of the suffix to have replication disabled')

  

+     repl_ruv_parser = repl_subcommands.add_parser('get-ruv', help='Get the database RUV entry for his suffix')

+     repl_ruv_parser.set_defaults(func=get_ruv)

+     repl_ruv_parser.add_argument('--suffix', required=True, help='The DN of the replicated suffix')

+ 

      repl_list_parser = repl_subcommands.add_parser('list', help='List all the replicated suffixes')

      repl_list_parser.set_defaults(func=list_suffixes)

  

@@ -7,16 +7,21 @@ 

  # --- END COPYRIGHT BLOCK ---

  

  from .config import baseconfig, configoperation

- from .sample import sampleentries, create_base_domain

- 

+ from .sample import (

+     sampleentries,

+     create_base_domain,

+     create_base_org,

+     create_base_orgunit,

+     create_base_cn,

+ )

  from lib389.idm.organizationalunit import OrganizationalUnits

  from lib389.idm.group import Groups

  from lib389.idm.posixgroup import PosixGroups

  from lib389.idm.user import nsUserAccounts

  from lib389.idm.services import ServiceAccounts

- 

  from lib389.idm.nscontainer import nsHiddenContainers

  

+ 

  class c001004000_sample_entries(sampleentries):

      def __init__(self, instance, basedn):

          super(c001004000_sample_entries, self).__init__(instance, basedn)
@@ -24,11 +29,27 @@ 

  

      # All checks done, apply!

      def _apply(self):

-         # Create the base domain object

-         domain = create_base_domain(self._instance, self._basedn)

-         domain.add('aci', [

+         suffix_rdn_attr = self._basedn.split('=')[0].lower()

+         if suffix_rdn_attr == 'dc':

+             suffix_obj = create_base_domain(self._instance, self._basedn)

+             aci_vals = ['dc', 'domain']

+         elif suffix_rdn_attr == 'o':

+             suffix_obj = create_base_org(self._instance, self._basedn)

+             aci_vals = ['o', 'organization']

+         elif suffix_rdn_attr == 'ou':

+             suffix_obj = create_base_orgunit(self._instance, self._basedn)

+             aci_vals = ['ou', 'organizationalunit']

+         elif suffix_rdn_attr == 'cn':

+             suffix_obj = create_base_cn(self._instance, self._basedn)

+             aci_vals = ['cn', 'nscontainer']

+         else:

+             # Unsupported rdn

+             raise ValueError("Suffix RDN is not supported for creating sample entries.  Only 'dc', 'o', 'ou', and 'cn' are supported.")

+ 

+         # Create the base object

+         suffix_obj.add('aci', [

              # Allow reading the base domain object

-             '(targetattr="dc || description || objectClass")(targetfilter="(objectClass=domain)")(version 3.0; acl "Enable anyone domain read"; allow (read, search, compare)(userdn="ldap:///anyone");)',

+             '(targetattr="' + aci_vals[0] + ' || description || objectClass")(targetfilter="(objectClass=' + aci_vals[1] + ')")(version 3.0; acl "Enable anyone domain read"; allow (read, search, compare)(userdn="ldap:///anyone");)',

              # Allow reading the ou

              '(targetattr="ou || objectClass")(targetfilter="(objectClass=organizationalUnit)")(version 3.0; acl "Enable anyone ou read"; allow (read, search, compare)(userdn="ldap:///anyone");)'

          ])

file modified
+34 -13
@@ -26,7 +26,7 @@ 

  from lib389.mappingTree import MappingTrees

  from lib389.agreement import Agreements

  from lib389.tombstone import Tombstones

- 

+ from lib389.tasks import CleanAllRUVTask

  from lib389.idm.domain import Domain

  from lib389.idm.group import Groups

  from lib389.idm.services import ServiceAccounts
@@ -897,9 +897,11 @@ 

              ruvs.append({"raw_ruv": self._rid_rawruv.get(rid),

                           "rid": rid,

                           "url": self._rid_url.get(rid),

-                          "csn": parse_csn(self._rid_csn.get(rid, '00000000000000000000')),

-                          "maxcsn": parse_csn(self._rid_maxcsn.get(rid, '00000000000000000000')),

-                          "modts": parse_csn(self._rid_modts.get(rid, '00000000'))})

+                          "csn": RUV().parse_csn(self._rid_csn.get(rid, '00000000000000000000')),

+                          "raw_csn": self._rid_csn.get(rid, '00000000000000000000'),

+                          "maxcsn": RUV().parse_csn(self._rid_maxcsn.get(rid, '00000000000000000000')),

+                          "raw_maxcsn": self._rid_maxcsn.get(rid, '00000000000000000000'),

+                          "modts": RUV().parse_csn(self._rid_modts.get(rid, '00000000'))})

          result["ruvs"] = ruvs

          return result

  
@@ -1153,13 +1155,27 @@ 

                  return False

          return True

  

+     def cleanRUV(self, rid):

+         """Run a cleanallruv task, only on a master, after deleting or demoting

+         it.  It is okay if it fails.

+         """

+         if rid != '65535':

+             properties = {'replica-base-dn': self.get_attr_val_utf8('nsDS5ReplicaRoot'),

+                           'replica-id': rid,

+                           'replica-force-cleaning': 'yes'}

+             try:

+                 clean_task = CleanAllRUVTask(self._instance)

+                 clean_task.create(properties=properties)

+             except ldap.LDAPError as e:

+                 self._log.debug("Failed to run cleanAllRUV task: " + str(e))

+ 

      def delete(self):

          """Delete a replica related to the provided suffix.

  

          If this replica role was ReplicaRole.HUB or ReplicaRole.MASTER, it

          also deletes the changelog associated to that replica. If it

          exists some replication agreement below that replica, they are

-         deleted.

+         deleted.  If this is a master we also clean the database ruv.

  

          :returns: None

          :raises: - InvalidArgumentError - if suffix is missing
@@ -1167,6 +1183,7 @@ 

          """

          # Delete the agreements

          self._delete_agreements()

+ 

          # Delete the replica

          return super(Replica, self).delete()

  
@@ -1265,14 +1282,14 @@ 

          elif replicarole == ReplicaRole.CONSUMER and newrole == ReplicaRole.MASTER:

              try:

                  self.replace_many((REPL_TYPE, str(REPLICA_RDWR_TYPE)),

-                                    (REPL_FLAGS, str(REPLICA_FLAGS_WRITE)),

-                                    (REPL_ID, str(rid)))

+                                   (REPL_FLAGS, str(REPLICA_FLAGS_WRITE)),

+                                   (REPL_ID, str(rid)))

              except ldap.LDAPError as e:

                  raise ValueError('Failed to update replica: ' + str(e))

          elif replicarole == ReplicaRole.HUB and newrole == ReplicaRole.MASTER:

              try:

                  self.replace_many((REPL_TYPE, str(REPLICA_RDWR_TYPE)),

-                                    (REPL_ID, str(rid)))

+                                   (REPL_ID, str(rid)))

              except ldap.LDAPError as e:

                  raise ValueError('Failed to update replica: ' + str(e))

  
@@ -1288,21 +1305,22 @@ 

  

          # Check the role type

          replicarole = self.get_role()

+         rid = self.get_attr_val_utf8(REPL_ID)

          if newrole.value >= replicarole.value:

              raise ValueError('Can not demote replica to higher or the same role: {} -> {}'.format(replicarole.name, newrole.name))

  

          # Demote it - set the replica type, flags and rid

          if replicarole == ReplicaRole.MASTER and newrole == ReplicaRole.HUB:

              try:

-                 self.replace_many([(REPL_TYPE, str(REPLICA_RDONLY_TYPE)),

-                                    (REPL_ID, str(CONSUMER_REPLICAID))])

+                 self.replace_many((REPL_TYPE, str(REPLICA_RDONLY_TYPE)),

+                                   (REPL_ID, str(CONSUMER_REPLICAID)))

              except ldap.LDAPError as e:

                  raise ValueError('Failed to update replica: ' + str(e))

          elif replicarole == ReplicaRole.MASTER and newrole == ReplicaRole.CONSUMER:

              try:

-                 self.replace_many([(REPL_TYPE, str(REPLICA_RDONLY_TYPE)),

-                                    (REPL_FLAGS, str(REPLICA_FLAGS_RDONLY)),

-                                    (REPL_ID, str(CONSUMER_REPLICAID))])

+                 self.replace_many((REPL_TYPE, str(REPLICA_RDONLY_TYPE)),

+                                   (REPL_FLAGS, str(REPLICA_FLAGS_RDONLY)),

+                                   (REPL_ID, str(CONSUMER_REPLICAID)))

              except ldap.LDAPError as e:

                  raise ValueError('Failed to update replica: ' + str(e))

          elif replicarole == ReplicaRole.HUB and newrole == ReplicaRole.CONSUMER:
@@ -1310,6 +1328,9 @@ 

                  self.set(REPL_FLAGS, str(REPLICA_FLAGS_RDONLY))

              except ldap.LDAPError as e:

                  raise ValueError('Failed to update replica: ' + str(e))

+         if replicarole == ReplicaRole.MASTER:

+             # We are no longer a master, clean up the old RID

+             self.cleanRUV(rid)

  

      def get_role(self):

          """Return the replica role

Description:

Ported the replication tab to React. Made many other improvements throughout the UI:

          - Protected "Treeviews" by disable/enable behavior as components are reloaded
          - Add a new Double Confirmation Modal/Popup
          - Added a script (buildAndWatch.sh) for faster/more convenient developing
          - Added a new RUV fucntion for the CLI, and made other lib389 improvements:
          - Added support for not only "dc" suffixes, but also "o", "ou", and "cn"

relates: https://pagure.io/389-ds-base/issue/50592

rebased onto ac3b16055476938b693f5a16c60580eb78f9525a

4 years ago

rebased onto 53a3919792b2c927753e24fa76abe4a74bbf0887

4 years ago

When 'master' replication is enabled, the 'Replication Changelog' tab is not updated. Can we trigger an update there?


Console error:

  1. Enable Master replication;
  2. Create an agreement with a name 't02' (not sure if it's related);
  3. Try to init the agreement (probably something wrong with a agreement name):

    Uncaught TypeError: cmd.startsWith is not a function
    at log_cmd (index.js:181209)
    at ReplAgmts.initAgmt (index.js:172781)
    at HTMLUnknownElement.callCallback (index.js:107984)
    at Object.invokeGuardedCallbackDev (index.js:108034)
    at invokeGuardedCallback (index.js:108091)
    at invokeGuardedCallbackAndCatchFirstError (index.js:108105)
    at executeDispatch (index.js:108396)
    at executeDispatchesInOrder (index.js:108415)
    at executeDispatchesAndRelease (index.js:108515)
    at executeDispatchesAndReleaseTopLevel (index.js:108523)

rebased onto 63c64f84e00b2ee521daacdc7807a722b561790b

4 years ago

@spichugi - fixed repl agmt issue issues, please review...

1 new commit added

  • Adjust suffix action downdown alignment
4 years ago
  1. Okay, once again Cockpit has updated the UI and some of the alignment is broken (most of the Replication tab). I think we should fix it in some other PR and then rebase this PR... Or we can fix it here. It's up to you...
    Either way, it is broken now (you saw it on a scrum)

  2. Enable Replication modal window:
    It states that either Bind DN and password OR Bind Group DN should be chosen.
    I think it should be clearly distinguished input wise. Maybe we can have it in different columns? Or we can grey out one of them if another has input... Either way...
    Now it just is not obvious for the user which one will be used when he hits the Enable Replication button.

  3. The console has some errors in it:

    [DOM] Found 10 elements with non-unique id #pagination-row-dropdown: (More info: https://goo.gl/9p2vKq)
    <button id=​"pagination-row-dropdown" role=​"button" aria-haspopup=​"true" aria-expanded=​"false" type=​"button" class=​"dropdown-toggle btn btn-default">​…​
    </button>​ <button id=​"pagination-row-dropdown" role=​"button" aria-haspopup=​"true" aria-expanded=​"false" type=​"button" class=​"dropdown-toggle btn btn-default">​…​</button>​
    

    I haven't seen it before. Could it be related to this PR?
    It has appeared when I've opened Replication tab and then pressed Enable Replication button.

  4. I think it makes sense to add some note or a hint to the Export LDIF button. The hint which points to the menu where we can restore from the LDIF. Not it is not connected by any words and it is a bit confusing. (later we can add a hyper link to another component and refresh the list of LDIFs - when we'll have the full react)

  5. I press Disable Replication button but the content stays the same after the loading is finished (I didn't have any agreements to begin with. I've tried to disable it just after the enablement). If I try to disable it again - the button just doesn't react.

  6. The [5] issue has appeared because I wanted to change replica ID. And it is impossible (I forgot to set it to another number). Additionally, can we remove the default RID from the field? It highly possible that the user will forget to change it while creating and then he will need to set once again and it can create a mess (if it is done too late in the Replication setup)

  7. Error updating replication configuration - Server is unwilling to perform - Invalid value for nsds5ReplicaPreciseTombstonePurging: true Value should be "on" or "off"

  8. I've tried to change the Changelog fields with some dummy values and it went bad:

        CMD: saveSettings: Applying replication changelog changes ==> dsconf -j 
            ldapi://%2fvar%2frun%2fslapd-master1.socket replication set-changelog --max-entries=1222 --encrypt=true
            cockpit.js:589 usage: dsconf [-h] [-v] [-D BINDDN] [-w BINDPW] [-W] [-y PWDFILE] [-b BASEDN]
                      [-Z] [-j]
                      instance
    
            dsconf: error: unrecognized arguments: --encrypt=true
    
            p @ cockpit.js:589
            m @ cockpit.js:599
           v @ cockpit.js:494 
            a.onmessage.o.dispatch_data @ cockpit.js:424
            e @ cockpit.js:280
            postMessage (async)
            (anonymous) @ index.js:11
            a.onmessage.o.dispatch_data @ cockpit.js:422
            VM524:1 Uncaught SyntaxError: Unexpected token u in JSON at position 0
              at JSON.parse (<anonymous>)
            at Function.<anonymous> (replChangelog.jsx:191)
            at s (cockpit.js:962)
            at cockpit.js:974
            at n (cockpit.js:880)
    

1 new commit added

  • Fix issues found by Simon, and fixed styling for cockpit changes
4 years ago

Okay, once again Cockpit has updated the UI and some of the alignment is broken (most of the Replication tab). I think we should fix it in some other PR and then rebase this PR... Or we can fix it here. It's up to you...
Either way, it is broken now (you saw it on a scrum)

Yeah "hr" and some headings have different css now :-(

Enable Replication modal window:
It states that either Bind DN and password OR Bind Group DN should be chosen.
I think it should be clearly distinguished input wise. Maybe we can have it in different columns? Or we can grey out one of them if another has input... Either way...

But you can set both. It's not one or the other. IPA uses both for example. Not sure how to make it "cleaner"

Now it just is not obvious for the user which one will be used when he hits the Enable Replication button.

The console has some errors in it:
[DOM] Found 10 elements with non-unique id #pagination-row-dropdown: (More info: https://goo.gl/9p2vKq)
<button id=​"pagination-row-dropdown" role=​"button" aria-haspopup=​"true" aria-expanded=​"false" type=​"button" class=​"dropdown-toggle btn btn-default">​…​
</button>​ <button id=​"pagination-row-dropdown" role=​"button" aria-haspopup=​"true" aria-expanded=​"false" type=​"button" class=​"dropdown-toggle btn btn-default">​…​</button>​

Right this a patternfly thing - nothing we can do about it

I haven't seen it before. Could it be related to this PR?
It has appeared when I've opened Replication tab and then pressed Enable Replication button.

I think it makes sense to add some note or a hint to the Export LDIF button. The hint which points to the menu where we can restore from the LDIF. Not it is not connected by any words and it is a bit confusing. (later we can add a hyper link to another component and refresh the list of LDIFs - when we'll have the full react)

Right, nothing we can do about it now, but I'll update the hint/title.

I press Disable Replication button but the content stays the same after the loading is finished (I didn't have any agreements to begin with. I've tried to disable it just after the enablement). If I try to disable it again - the button just doesn't react.

After fixing a reload issue I can not reproduce this.

The [5] issue has appeared because I wanted to change replica ID. And it is impossible (I forgot to set it to another number). Additionally, can we remove the default RID from the field? It highly possible that the user will forget to change it while creating and then he will need to set once again and it can create a mess (if it is done too late in the Replication setup)

Well I want to use a "number" input field so no one can enter invalid values. I'm not sure what your concern is here though. If you enter the wrong rid yes you must start over. But the default value is fine. You can have different backends use the same same rid. It's the remote replica that can not reuse a rid for the same suffix. I see no easy way to pick a "better" default RID.

Error updating replication configuration - Server is unwilling to perform - Invalid value for nsds5ReplicaPreciseTombstonePurging: true Value should be "on" or "off"

I've tried to change the Changelog fields with some dummy values and it went bad:
CMD: saveSettings: Applying replication changelog changes ==> dsconf -j
ldapi://%2fvar%2frun%2fslapd-master1.socket replication set-changelog --max-entries=1222 --encrypt=true
cockpit.js:589 usage: dsconf [-h] [-v] [-D BINDDN] [-w BINDPW] [-W] [-y PWDFILE] [-b BASEDN]
[-Z] [-j]
instance

    dsconf: error: unrecognized arguments: --encrypt=true

    p @ cockpit.js:589
    m @ cockpit.js:599
   v @ cockpit.js:494 
    a.onmessage.o.dispatch_data @ cockpit.js:424
    e @ cockpit.js:280
    postMessage (async)
    (anonymous) @ index.js:11
    a.onmessage.o.dispatch_data @ cockpit.js:422
    VM524:1 Uncaught SyntaxError: Unexpected token u in JSON at position 0
      at JSON.parse (<anonymous>)
    at Function.<anonymous> (replChangelog.jsx:191)
    at s (cockpit.js:962)
    at cockpit.js:974
    at n (cockpit.js:880)

Ahh, yeah encrypting the changelog is not implemented in dsconf yet. I'll remove the option from the UI

Well changes made, please review...

It states that either Bind DN and password OR Bind Group DN should be chosen.
I think it should be clearly distinguished input wise. Maybe we can have it in different columns? Or we can grey out one of them if another has input... Either way...

But you can set both. It's not one or the other. IPA uses both for example. Not sure how to make it "cleaner"

Okay, can we then update the text in the modal window? It's a bit confusing because it says or... Or I misunderstood something.

Well I want to use a "number" input field so no one can enter invalid values. I'm not sure what your concern is here though. If you enter the wrong rid yes you must start over. But the default value is fine. You can have different backends use the same same rid. It's the remote replica that can not reuse a rid for the same suffix. I see no easy way to pick a "better" default RID.

I was thinking about setting some 'blank' value there. Maybe 0 then? So the user is forced to update it.

I'll finish the review tomorrow morning!

It states that either Bind DN and password OR Bind Group DN should be chosen.
I think it should be clearly distinguished input wise. Maybe we can have it in different columns? Or we can grey out one of them if another has input... Either way...
But you can set both. It's not one or the other. IPA uses both for example. Not sure how to make it "cleaner"

Okay, can we then update the text in the modal window? It's a bit confusing because it says or... Or I misunderstood something.

It can be cleaned up, but I wanted you to know that having one or the other or both is acceptable.

Well I want to use a "number" input field so no one can enter invalid values. I'm not sure what your concern is here though. If you enter the wrong rid yes you must start over. But the default value is fine. You can have different backends use the same same rid. It's the remote replica that can not reuse a rid for the same suffix. I see no easy way to pick a "better" default RID.

I was thinking about setting some 'blank' value there. Maybe 0 then? So the user is forced to update it.

Well with the "number" input field they want a min and max value. So it sort of needs to be 1 through 65534. Like I said it really doesn't matter what it defaults too. Most customers only replicate one backend anyway. Even if you have 10 backends, they can all use rid 1 if they want to. You just can't have multiple masters for the same suffix using the same rid. Seems overkill to try and query all the masters recursively to provide a valid range across the topology when enabling replication on a suffix in the UI.

Well with the "number" input field they want a min and max value. So it sort of needs to be 1 through 65534. Like I said it really doesn't matter what it defaults too. Most customers only replicate one backend anyway. Even if you have 10 backends, they can all use rid 1 if they want to. You just can't have multiple masters for the same suffix using the same rid. Seems overkill to try and query all the masters recursively to provide a valid range across the topology when enabling replication on a suffix in the UI.

Okay, agree! Probably I am overthinking the subject...

So about a few issues I've found:

  1. While trying to set `Fast Tombstone Purging':

    Uncaught TypeError: config.attrs.nsds5replicaprecisetombstonepurging.lower is not a function
    at Function.<anonymous> (index.js:184254)
    at s (cockpit.js:962)
    at cockpit.js:974
    at n (cockpit.js:880)
    
  2. Winsync agreements initialization suffer from the same issue that was fixed for our agmts...

    Uncaught TypeError: cmd.startsWith is not a function
    
  3. When I try to demote master to consumer it demotes it but the error is displayed - {"desc": "bad operand type for unary +: 'str'"}

Aand I think that's it. Other stuff works and looks good!

1 new commit added

  • Next round of fixes
4 years ago

@spichugi - committed next round of fixes, please review...

1 new commit added

  • Remove cleanAllRUV from replication disable code
4 years ago

rebased onto 5e48e9f

4 years ago

Pull-Request has been merged by mreynolds

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

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 55
+2 -0
file changed
rpm/389-ds-base.spec.in
+45
file added
src/cockpit/389-console/buildAndRun.sh
+74 -22
file changed
src/cockpit/389-console/src/css/ds.css
+61 -10
file changed
src/cockpit/389-console/src/database.jsx
+5 -39
file changed
src/cockpit/389-console/src/ds.js
+7 -0
file changed
src/cockpit/389-console/src/index.es6
+13 -15
file changed
src/cockpit/389-console/src/index.html
+1 -2
file changed
src/cockpit/389-console/src/lib/database/attrEncryption.jsx
+68 -33
file changed
src/cockpit/389-console/src/lib/database/backups.jsx
+373 -219
file changed
src/cockpit/389-console/src/lib/database/chaining.jsx
+28 -24
file changed
src/cockpit/389-console/src/lib/database/databaseConfig.jsx
+16 -9
file changed
src/cockpit/389-console/src/lib/database/databaseModal.jsx
+0 -2
file changed
src/cockpit/389-console/src/lib/database/indexes.jsx
+7 -13
file changed
src/cockpit/389-console/src/lib/database/referrals.jsx
+64 -32
file changed
src/cockpit/389-console/src/lib/database/suffix.jsx
+43 -36
file changed
src/cockpit/389-console/src/lib/database/suffixConfig.jsx
+5 -10
file changed
src/cockpit/389-console/src/lib/database/vlvIndexes.jsx
+8 -3
file changed
src/cockpit/389-console/src/lib/monitor/accesslog.jsx
+10 -5
file changed
src/cockpit/389-console/src/lib/monitor/auditfaillog.jsx
+10 -5
file changed
src/cockpit/389-console/src/lib/monitor/auditlog.jsx
+10 -5
file changed
src/cockpit/389-console/src/lib/monitor/chainingMonitor.jsx
+52 -72
file changed
src/cockpit/389-console/src/lib/monitor/dbMonitor.jsx
+9 -5
file changed
src/cockpit/389-console/src/lib/monitor/errorlog.jsx
+5 -10
file changed
src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
+8 -2
file changed
src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
+14 -7
file changed
src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx
+63 -56
file changed
src/cockpit/389-console/src/lib/monitor/snmpMonitor.jsx
+46 -63
file changed
src/cockpit/389-console/src/lib/monitor/suffixMonitor.jsx
+123 -0
file changed
src/cockpit/389-console/src/lib/notifications.jsx
+3 -2
file changed
src/cockpit/389-console/src/lib/plugins/pluginBasicConfig.jsx
+1150
file added
src/cockpit/389-console/src/lib/replication/replAgmts.jsx
+395
file added
src/cockpit/389-console/src/lib/replication/replChangelog.jsx
+640
file added
src/cockpit/389-console/src/lib/replication/replConfig.jsx
+1684
file added
src/cockpit/389-console/src/lib/replication/replModals.jsx
+456
file added
src/cockpit/389-console/src/lib/replication/replSuffix.jsx
+608
file added
src/cockpit/389-console/src/lib/replication/replTables.jsx
+314
file added
src/cockpit/389-console/src/lib/replication/replTasks.jsx
+1210
file added
src/cockpit/389-console/src/lib/replication/winsyncAgmts.jsx
+38 -15
file changed
src/cockpit/389-console/src/lib/security/certificateManagement.jsx
+8 -1
file changed
src/cockpit/389-console/src/lib/tools.jsx
+48 -21
file changed
src/cockpit/389-console/src/monitor.jsx
-724
file removed
src/cockpit/389-console/src/replication.html
-2207
file removed
src/cockpit/389-console/src/replication.js
+1148
file added
src/cockpit/389-console/src/replication.jsx
+1 -1
file changed
src/cockpit/389-console/src/security.jsx
+2 -2
file changed
src/cockpit/389-console/src/servers.html
+0 -2
file changed
src/cockpit/389-console/webpack.config.js
+5 -5
file changed
src/lib389/lib389/agreement.py
+1 -1
file changed
src/lib389/lib389/backend.py
+1 -1
file changed
src/lib389/lib389/chaining.py
+24 -12
file changed
src/lib389/lib389/cli_conf/backend.py
+2 -2
file changed
src/lib389/lib389/cli_conf/monitor.py
+26 -2
file changed
src/lib389/lib389/cli_conf/replication.py
+28 -7
file changed
src/lib389/lib389/configurations/config_001004000.py
+34 -13
file changed
src/lib389/lib389/replica.py