Browse Source

Ticket 50289 - Fix various database UI issues

Description:

Fixed these issues:

- https://bugzilla.redhat.com/show_bug.cgi?id=1664621 - backup freezes when no suffix present

- https://bugzilla.redhat.com/show_bug.cgi?id=1685395 - Perform Backup fails when Backend Name is not configured

- https://bugzilla.redhat.com/show_bug.cgi?id=1688587 - typo when restarting instance

- https://bugzilla.redhat.com/show_bug.cgi?id=1688775 - db tree breaks when suffix contains spaces.

- https://bugzilla.redhat.com/show_bug.cgi?id=1688919 - backups fail with empty name

Also fixed issue where if you start an instance in UI the configuration is correctly loaded.

https://pagure.io/389-ds-base/issue/50289

Reviewed by: spichugi(Thanks!)
Mark Reynolds 6 years ago
parent
commit
395a4a26bd

+ 8 - 7
ldap/servers/slapd/task.c

@@ -1486,7 +1486,7 @@ task_backup_add(Slapi_PBlock *pb __attribute__((unused)),
                 Slapi_Entry *e,
                 Slapi_Entry *eAfter __attribute__((unused)),
                 int *returncode,
-                char *returntext __attribute__((unused)),
+                char *returntext,
                 void *arg __attribute__((unused)))
 {
     Slapi_Backend *be = NULL;
@@ -1529,18 +1529,19 @@ task_backup_add(Slapi_PBlock *pb __attribute__((unused)),
     }
     slapi_ch_free_string(&cookie);
     if (NULL == be || NULL == be->be_database->plg_db2archive) {
-        slapi_log_err(SLAPI_LOG_ERR,
-                      "task_backup_add", "no db2archive function defined.\n");
+        PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+                "no db2archive function defined.  There is no backend/suffix present");
+        slapi_log_err(SLAPI_LOG_ERR, "task_backup_add", "Error: %s\n", returntext);
         *returncode = LDAP_UNWILLING_TO_PERFORM;
         rv = SLAPI_DSE_CALLBACK_ERROR;
         goto out;
     }
 
     if (!SLAPI_PLUGIN_IS_V3(be->be_database)) {
-        slapi_log_err(SLAPI_LOG_ERR,
-                      "task_backup_add", "Can't perform an backup with pre-V3 "
-                                         "backend plugin %s\n",
-                      be->be_database->plg_name);
+        PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+                "Can't perform an backup with pre-V3 backend plugin %s\n",
+                be->be_database->plg_name);
+        slapi_log_err(SLAPI_LOG_ERR, "task_backup_add", "Error: %s\n", returntext);
         *returncode = LDAP_UNWILLING_TO_PERFORM;
         rv = SLAPI_DSE_CALLBACK_ERROR;
         goto out;

+ 18 - 14
src/cockpit/389-console/src/database.jsx

@@ -97,11 +97,13 @@ export class Database extends React.Component {
     }
 
     componentWillMount () {
-        this.loadGlobalConfig();
-        this.loadChainingConfig();
-        this.loadLDIFs();
-        this.loadBackups();
-        this.loadSuffixList();
+        if (!this.state.loaded) {
+            this.loadGlobalConfig();
+            this.loadChainingConfig();
+            this.loadLDIFs();
+            this.loadBackups();
+            this.loadSuffixList();
+        }
     }
 
     componentDidMount() {
@@ -183,7 +185,7 @@ export class Database extends React.Component {
                         }), this.setState({configUpdated: 0}));
                 })
                 .fail(err => {
-                    this.props.addNotification(
+                    this.addNotification(
                         "error",
                         `Error loading database configuration - ${err}`
                     );
@@ -269,7 +271,7 @@ export class Database extends React.Component {
                     ), this.loadAvailableControls());
                 })
                 .fail(err => {
-                    this.props.addNotification(
+                    this.addNotification(
                         "error",
                         `Error loading default chaining configuration - ${err}`
                     );
@@ -313,11 +315,14 @@ export class Database extends React.Component {
             "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
             "backend", "get-tree",
         ];
-        log_cmd("getTree", "Start building the suffix tree", cmd);
+        log_cmd("loadSuffixTree", "Start building the suffix tree", cmd);
         cockpit
                 .spawn(cmd, { superuser: true, err: "message" })
                 .done(content => {
-                    let treeData = JSON.parse(content);
+                    let treeData = [];
+                    if (content != "") {
+                        treeData = JSON.parse(content);
+                    }
                     let basicData = [
                         {
                             text: "Global Database Configuration",
@@ -356,7 +361,6 @@ export class Database extends React.Component {
                     this.setState(() => ({
                         nodes: basicData,
                         node_name: current_node,
-
                     }), this.update_tree_nodes);
                 });
     }
@@ -770,7 +774,7 @@ export class Database extends React.Component {
                     });
                 })
                 .fail(err => {
-                    this.props.addNotification(
+                    this.addNotification(
                         "error",
                         `Error loading indexes for ${suffix} - ${err}`
                     );
@@ -924,7 +928,7 @@ export class Database extends React.Component {
                                                         });
                                                     })
                                                     .fail(err => {
-                                                        this.props.addNotification(
+                                                        this.addNotification(
                                                             "error",
                                                             `Error loading indexes for ${suffix} - ${err}`
                                                         );
@@ -960,7 +964,7 @@ export class Database extends React.Component {
         const cmd = [
             "dsctl", "-j", this.props.serverId, "backups"
         ];
-        log_cmd("loadLDIFs", "Load Backups", cmd);
+        log_cmd("loadBackups", "Load Backups", cmd);
         cockpit
                 .spawn(cmd, { superuser: true, err: "message" })
                 .done(content => {
@@ -995,7 +999,7 @@ export class Database extends React.Component {
                     });
                 })
                 .fail(err => {
-                    this.props.addNotification(
+                    this.addNotification(
                         "error",
                         `Failed to get attributes - ${err}`
                     );

+ 8 - 8
src/cockpit/389-console/src/ds.js

@@ -309,13 +309,6 @@ function popup_success(msg) {
 // all the save functions for all the pages here.  This is not used for modal forms
 function save_all () {
   save_config();  // Server Config Page
-  //
-  // TODO:
-  //   save_chaining();
-  //   save_chaining_suffix();
-  //   save_global_backend();
-  //   save_suffix();
-  //   save_security();
 }
 
 function load_repl_suffix_dropdowns() {
@@ -351,7 +344,7 @@ function load_repl_suffix_dropdowns() {
 
 var loading_cfg = 0;
 
-function load_config (){
+function load_config (refresh){
   // If we are currently loading config don't do it twice
   if (loading_cfg == 1){
     return;
@@ -418,6 +411,13 @@ function load_config (){
         $("#server-config").show();
         clearInterval(loading_config);
         loading_cfg = 0;
+
+        if (refresh) {
+            // Reload reactJS pages by clicking dummy element
+            let reload_el = document.getElementById('reload-page');
+            reload_el.click();
+        }
+
         console.log("Completed configuration initialization.");
       }
     }, 300);

+ 5 - 6
src/cockpit/389-console/src/index.es6

@@ -14,16 +14,18 @@ function renderReactDOM(clear) {
                 .getElementById("select-server")
                 .value.replace("slapd-", "");
     }
+    let d = new Date();
+    let n = d.getTime(); // might not be needed MARK
 
     // Plugins Tab
     ReactDOM.render(
-        <Plugins serverId={serverIdElem} />,
+        <Plugins serverId={serverIdElem} key={n} />,
         document.getElementById("plugins")
     );
 
     // Database tab
     ReactDOM.render(
-        <Database serverId={serverIdElem} />,
+        <Database serverId={serverIdElem} key={n} />,
         document.getElementById("database")
     );
 }
@@ -45,10 +47,7 @@ document.addEventListener("DOMContentLoaded", function() {
                     .getElementById("select-server")
                     .addEventListener("change", renderReactWrapper);
             document
-                    .getElementById("start-server-btn")
-                    .addEventListener("click", renderReactWrapper);
-            document
-                    .getElementById("restart-server-btn")
+                    .getElementById("reload-page")
                     .addEventListener("click", renderReactWrapper);
             document
                     .getElementById("remove-server-btn")

+ 1 - 0
src/cockpit/389-console/src/index.html

@@ -35,6 +35,7 @@
 </head>
 
 <body>
+  <div id="reload-page" hidden></div>
   <div id="loading-page" class="ds-center ds-loading-spinner" hidden>
     <h3 id="loading-msg">Loading Directory Server Configuration...</h3>
     <p><span class="spinner spinner-lg spinner-inline"></span></p>

+ 12 - 5
src/cockpit/389-console/src/lib/database/backups.jsx

@@ -86,6 +86,7 @@ export class Backups extends React.Component {
         this.setState({
             showExportModal: true,
             exportSpinner: false,
+            ldifName: "",
             ldifSuffix: this.props.suffixes[0]
         });
     }
@@ -147,7 +148,8 @@ export class Backups extends React.Component {
     showBackupModal () {
         this.setState({
             showBackupModal: true,
-            backupSpinning: false
+            backupSpinning: false,
+            backupName: ""
         });
     }
 
@@ -289,10 +291,15 @@ export class Backups extends React.Component {
             backupSpinning: true
         });
 
-        const cmd = [
+        let cmd = [
             "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
-            "backup", "create", this.state.backupName
+            "backup", "create"
         ];
+
+        if (this.state.backupName != "") {
+            cmd.push(this.state.backupName);
+        }
+
         log_cmd("doBackup", "Add backup task", cmd);
         cockpit
                 .spawn(cmd, { superuser: true, err: "message" })
@@ -615,7 +622,7 @@ class ExportModal extends React.Component {
                                 </Col>
                             </Row>
                             <p />
-                            <Row title="Name of exported LDIF file">
+                            <Row 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>
@@ -691,7 +698,7 @@ export class BackupModal extends React.Component {
                     </Modal.Header>
                     <Modal.Body>
                         <Form horizontal autoComplete="off">
-                            <Row title="LDIF file to import">
+                            <Row title="Backup name, if left blank the date and time will be used as the name">
                                 <Col sm={3}>
                                     <ControlLabel>Backup Name</ControlLabel>
                                 </Col>

+ 1 - 1
src/cockpit/389-console/src/plugins.jsx

@@ -182,7 +182,7 @@ export class Plugins extends React.Component {
                 })
                 .fail(err => {
                     if (err != 0) {
-                        console.error("pluginList failed", err);
+                        console.log("pluginList failed", err);
                     }
                     this.toggleLoading();
                 });

+ 4 - 4
src/cockpit/389-console/src/servers.js

@@ -416,7 +416,7 @@ function do_backup(server_inst, backup_name) {
   done(function(status_data) {
     var status_json = JSON.parse(status_data);
     if (status_json.running == true) {
-      var cmd = [DSCONF, server_inst, 'backup', 'create',  backup_name];
+      var cmd = [DSCONF, "-j", server_inst, 'backup', 'create',  backup_name];
       log_cmd('#ds-backup-btn (click)', 'Backup server instance', cmd);
       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).
       done(function(data) {
@@ -471,7 +471,7 @@ $(document).ready( function() {
           log_cmd('#start-server-btn (click)', 'Start server instance', cmd);
           cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {
             $("#start-instance-form").modal('toggle');
-            load_config();
+            load_config(true);
             popup_success("Started instance \"" + server_id + "\"");
           }).fail(function(data) {
             $("#start-instance-form").modal('toggle');
@@ -497,13 +497,13 @@ $(document).ready( function() {
 
 
         document.getElementById("restart-server-btn").addEventListener("click", function() {
-          $("#ds-restart-inst").html("<span class=\"spinner spinner-xs spinner-inline\"></span> Retarting instance <b>" + server_id + "</b>...");
+          $("#ds-restart-inst").html("<span class=\"spinner spinner-xs spinner-inline\"></span> Restarting instance <b>" + server_id + "</b>...");
           $("#restart-instance-form").modal('toggle');
           var cmd = [DSCTL, server_inst, 'restart'];
           log_cmd('#restart-server-btn (click)', 'Restart server instance', cmd);
           cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {
             $("#restart-instance-form").modal('toggle');
-            load_config();
+            load_config(true);
             popup_success("Restarted instance \"" + server_id + "\"");
           }).fail(function(data) {
             $("#restart-instance-form").modal('toggle');

+ 14 - 0
src/lib389/lib389/backend.py

@@ -480,8 +480,20 @@ class Backend(DSLdapObject):
         :returns: DSLdapObject of the created entry
         """
 
+        # normalize suffix (remove spaces between comps)
+        if dn is not None:
+            dn_comps = ldap.dn.explode_dn(dn.lower())
+            dn = ",".join(dn_comps)
+
+        if properties is not None:
+            suffix_dn = properties['nsslapd-suffix'].lower()
+            dn_comps = ldap.dn.explode_dn(suffix_dn)
+            ndn = ",".join(dn_comps)
+            properties['nsslapd-suffix'] = ndn
+
         sample_entries = properties.pop(BACKEND_SAMPLE_ENTRIES, False)
         parent_suffix = properties.pop('parent', False)
+
         # Okay, now try to make the backend.
         super(Backend, self).create(dn, properties, basedn)
 
@@ -798,6 +810,8 @@ class Backends(DSLdapObjects):
 
         task = ExportTask(self._instance)
         task_properties = {'nsInstance': be_names}
+        if ldif == "":
+            ldif = None
         if ldif is not None and not ldif.startswith("/"):
             if ldif.endswith(".ldif"):
                 task_properties['nsFilename'] = os.path.join(self._instance.ds_paths.ldif_dir, ldif)

+ 6 - 1
src/lib389/lib389/cli_conf/backend.py

@@ -376,9 +376,14 @@ def backend_get_tree(inst, basedn, log, args):
     for be in be_insts:
         suffix = be.get_attr_val_utf8_l('nsslapd-suffix')
         be_name = be.get_attr_val_utf8('cn')
-        mt = be._mts.get(suffix)
+        try:
+            mt = be._mts.get(suffix)
+        except ldap.NO_SUCH_OBJECT:
+            log.debug("Failed to find the mapping tree entry using this suffix: {}".format(suffix))
+            continue
         sub = mt.get_attr_val_utf8_l('nsslapd-parent-suffix')
         if sub is not None:
+            # Skip sub suffixes for now, we will get them later
             continue
         nodes.append(build_node(suffix, be_name))
 

+ 42 - 42
src/lib389/lib389/pwpolicy.py

@@ -24,48 +24,48 @@ class PwPolicyManager(object):
     def __init__(self, instance):
         self._instance = instance
         self.log = instance.log
-        self.pwp_attributes = [
-            'passwordstoragescheme',
-            'passwordChange',
-            'passwordMustChange',
-            'passwordHistory',
-            'passwordInHistory',
-            'passwordAdminDN',
-            'passwordTrackUpdateTime',
-            'passwordWarning',
-            'passwordMaxAge',
-            'passwordMinAge',
-            'passwordExp',
-            'passwordGraceLimit',
-            'passwordSendExpiringTime',
-            'passwordLockout',
-            'passwordUnlock',
-            'passwordMaxFailure',
-            'passwordLockoutDuration',
-            'passwordResetFailureCount',
-            'passwordCheckSyntax',
-            'passwordMinLength',
-            'passwordMinDigits',
-            'passwordMinAlphas',
-            'passwordMinUppers',
-            'passwordMinLowers',
-            'passwordMinSpecials',
-            'passwordMaxRepeats',
-            'passwordMin8bit',
-            'passwordMinCategories',
-            'passwordMinTokenLength',
-            'passwordDictPath',
-            'passwordDictCheck',
-            'passwordPalindrome',
-            'passwordMaxSequence',
-            'passwordMaxClassChars',
-            'passwordMaxSeqSets',
-            'passwordBadWords',
-            'passwordUserAttributes',
-            'passwordIsGlobalPolicy',
-            'nsslapd-pwpolicy-local',
-            'nsslapd-allow-hashed-passwords'
-        ]
+        self.arg_to_attr = {
+            'pwdlocal': 'nsslapd-pwpolicy-local',
+            'pwdscheme': 'passwordstoragescheme',
+            'pwdchange': 'passwordChange',
+            'pwdmustchange': 'passwordMustChange',
+            'pwdhistory': 'passwordHistory',
+            'pwdhistorycount': 'passwordInHistory',
+            'pwdadmin': 'passwordAdminDN',
+            'pwdtrack': 'passwordTrackUpdateTime',
+            'pwdwarning': 'passwordWarning',
+            'pwdisglobal': 'passwordIsGlobalPolicy',
+            'pwdexpire': 'passwordExp',
+            'pwdmaxage': 'passwordMaxAge',
+            'pwdminage': 'passwordMinAge',
+            'pwdgracelimit': 'passwordGraceLimit',
+            'pwdsendexpiring': 'passwordSendExpiringTime',
+            'pwdlockout': 'passwordLockout',
+            'pwdunlock': 'passwordUnlock',
+            'pwdlockoutduration': 'passwordLockoutDuration',
+            'pwdmaxfailures': 'passwordMaxFailure',
+            'pwdresetfailcount': 'passwordResetFailureCount',
+            'pwdchecksyntax': 'passwordCheckSyntax',
+            'pwdminlen': 'passwordMinLength',
+            'pwdmindigits': 'passwordMinDigits',
+            'pwdminalphas': 'passwordMinAlphas',
+            'pwdminuppers': 'passwordMinUppers',
+            'pwdminlowers': 'passwordMinLowers',
+            'pwdminspecials': 'passwordMinSpecials',
+            'pwdmin8bits': 'passwordMin8bit',
+            'pwdmaxrepeats': 'passwordMaxRepeats',
+            'pwdpalindrome': 'passwordPalindrome',
+            'pwdmaxseq': 'passwordMaxSequence',
+            'pwdmaxseqsets': 'passwordMaxSeqSets',
+            'pwdmaxclasschars': 'passwordMaxClassChars',
+            'pwdmincatagories': 'passwordMinCategories',
+            'pwdmintokenlen': 'passwordMinTokenLength',
+            'pwdbadwords': 'passwordBadWords',
+            'pwduserattrs': 'passwordUserAttributes',
+            'pwddictcheck': 'passwordDictCheck',
+            'pwddictpath': 'passwordDictPath',
+            'pwdallowhash': 'nsslapd-allow-hashed-passwords'
+        }
 
     def is_subtree_policy(self, dn):
         """Check if the entry has a subtree password policy.  If we can find a