From c6198c5397d185cf9aeda7e942db952089c613ed Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Feb 13 2020 15:47:43 +0000 Subject: Issue 50855 - UI: Port Server Tab to React Description: Ported the server tab to reactJS. Also made other changes: - Moved Password policy to the database tab tree. - Moved the Security Tab to the Server Tab tree. - Fixed all the typeAhead errors - Removed unused CSS classes relates: https://pagure.io/389-ds-base/issue/50855 Reviewed by: spichugi(Thanks!) --- diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css index a71f615..82b1ab3 100644 --- a/src/cockpit/389-console/src/css/ds.css +++ b/src/cockpit/389-console/src/css/ds.css @@ -20,7 +20,7 @@ } .ds-left-align { - right: 0 !important; + text-align: left !important; } /* Main nav page index.html */ @@ -37,36 +37,11 @@ margin-left: 65px; } -.ds-oc-add-del-btn { - padding: 0 !important; - margin: 10px !important; - width: 130px; - height: 26px; - text-align: center; -} - -.ds-oc-must-buttons { - padding: 0 !important; - padding-left: 3px; - margin-top: 70px; - margin-bottom: 75px; - margin-left: 5px; - margin-right: 9px; -} - /* This can probably be removed once we switch to PF React 4 - suffix action button */ .ds-action-button { color: white; } -.ds-oc-may-buttons { - padding0: !important; - padding-left: 3px; - margin-top: 4px; - margin-left: 5px; - margin-right: 9px; -} - .ds-refresh { background-color: #0088ce; background-image: linear-gradient(to bottom,#39a5dc 0,#0088ce 100%); @@ -124,6 +99,10 @@ td { padding: 0px !important; } +.ds-width-sm { + max-width: 100px; +} + .ds-tree { font-size: 0.5; background-color: #f8f8f8; @@ -164,12 +143,6 @@ td { padding-left: 5px; } -.ds-input-auto-good { - width: 100%; - border-color: green; - padding-left: 5px; -} - .ds-input-right { text-align: right; } @@ -182,14 +155,6 @@ td { max-width: 65px !important; } -.ds-lag-report { - min-width: 800px !important; -} - -.ds-repl-mgr { - max-width: 600px !important; -} - .ds-history-input { margin-top: !important; margin-right: 5px; @@ -204,53 +169,10 @@ td { width: 50px; } -.ds-divider-med { - width: 15px; -} - -.ds-repl-managers-list { - width: 420px; - height: 200px; -} - -.ds-repl-managers-button { - margin-top: -15px; - margin-bottom: 10px; - margin-left: 0px; -} - -.ds-repl-manager-checkbox { - margin-top: 20px !important; - padding-top: 20px !important; - margin-left: 10px !important; - margin-right: 10px !important; -} - .ds-label { vertical-align: middle !important; /* Fixes any weird issues in Firefox and IE */ } -/* Replication styles */ -.ds-agmt-dropdown { - padding: 0px !important; - line-height: 0 !important; - overflow: visible !important; - width: auto !important; -} - -.ds-oc-dropdown { - margin-top: 5px; - color: #181818 !important; - padding: 0px !important; - line-height: 0 !important; - height: 30px; - max-height: 200px !important; - width: 315px !important; - text-align: left; - outline: 0 !important; - overflow: auto; -} - .ds-dblink-dropdown { width: 140px !important; text-align: left; @@ -284,15 +206,6 @@ td { line-height: 1; } -.ds-sasl-table { - background-color: white !important; - padding: 0px; - border: 1px solid #909090; - table-layout: fixed; - clear: both; - word-wrap: break-word !important; - text-align: center; -} .ds-db-table { border: 1px solid #d1d1d1; @@ -357,36 +270,12 @@ td { margin-left: 0 !important; } -.ds-fractional-list { - width: 230px; - min-height: 100px !important; - max-height: 100px !important; - overflow-y: scroll; -} - -.ds-fractional-btn { - width: 150px; - height: 30px; - margin: 5px 5px 5px 10px; -} - -.ds-agmt-schedule-checkbox { - margin: 8px !important; - padding: 5px; -} - .ds-config-checkbox { margin-top: 10px !important; margin-bottom: 10px !important; margin-right: 5px !important; } -.ds-server-checkbox { - margin-top: 15px !important; - margin-right: 12px !important; - padding: 5px; -} - .ds-send-expiring-checkbox { margin-top: 12px !important; margin-right: 12px !important; @@ -411,12 +300,6 @@ td { margin-bottom: 10px !important; } -.ds-config-label-med { - margin-top: 10px; - width: 125px !important; - margin-bottom: 10px !important; -} - .ds-config-sub-label { margin-top: 10px; width: 225px !important; @@ -429,11 +312,6 @@ td { margin-bottom: 10px !important; } -.ds-cleanallruv-label { - width: 150px !important; - margin-bottom: 10px !important; -} - .ds-expire-label { margin-top: 7px; margin-bottom: 7px; @@ -444,30 +322,10 @@ td { padding: 10px; } -.ds-config-diskmon-label { - width: 210px !important; - padding-left: 30px; - margin-top: 10px; -} - -.ds-config-indent-sm-label { - margin-top: 10px; - margin-bottom: 10px; - width: 195px; - margin-left: 40px !important; - padding-right: 10px !important; -} - .ds-expired-div { padding-left: 30px !important; } -.ds-config-diskmon-checkbox { - margin: 12px !important; - padding: 5px; - margin-left: 30px !important; -} - .ds-modal-row { margin-left: 20px; margin-right: 0px !important; @@ -497,13 +355,6 @@ td { min-height: 350px !important; } -.ds-may-must-list { - width: 232px !important; - height: 150px !important; - margin: 0 !important; - padding: 0 !important; -} - p { line-height: 1; white-space: normal; @@ -676,10 +527,6 @@ option { background-color: transparent !important; } -.ds-accordion-panel { - margin-top: 10px; -} - .ds-margin-top { margin-top: 10px !important; } @@ -775,24 +622,11 @@ option { margin-left: 5px !important; } -.ds-td { - padding-left: 10px !important; - padding-top: 8px !important; - padding-bottom: 0px !important; - valign: middle !important; -} - -.ds-mgr-table { - max-width: 600px; - min-width: 600px; -} - .ds-loglevel-table { max-width: 500px; min-width: 500px; table-layout: fixed; margin-top: 20px; - margin-left: 20px; } .ds-table-checkbox { @@ -800,14 +634,6 @@ option { text-align: center; } -.ds-page-content { - margin-top: 30px !important; -} - -.ds-spacing-sm { - margin-right: 10px; -} - .navbar-default { margin-bottom: 0px; } @@ -832,28 +658,10 @@ option { margin-left: 140px !important; } -.ds-nested-modal { - margin-top: 100px; - margin-left: 100px; - min-width: 275px; -} - -.ds-attr-select { - min-height: 300px; - max-height: 300px; - width: 265px; - overflow: auto; -} - .ds-center { text-align: center; } -.ds-trailing-btn { - margin-left: 10px; - margin-top: -2px; -} - .ds-loading-spinner { position: fixed; top: 25%; @@ -889,6 +697,10 @@ option { margin-top: -3px; } +.ds-lower-field { + margin-top: 3px; +} + .content-view-pf-pagination > div > span:last-child { position: relative; } @@ -995,7 +807,7 @@ option { } .ds-suffix-header { - font-size: 18px; + font-size: 20px; margin-bottom: 15px; padding-top: 5px; } diff --git a/src/cockpit/389-console/src/database.jsx b/src/cockpit/389-console/src/database.jsx index e359626..3ac1e7a 100644 --- a/src/cockpit/389-console/src/database.jsx +++ b/src/cockpit/389-console/src/database.jsx @@ -9,6 +9,8 @@ import { import { GlobalDatabaseConfig } from "./lib/database/databaseConfig.jsx"; import { Suffix } from "./lib/database/suffix.jsx"; import { Backups } from "./lib/database/backups.jsx"; +import { GlobalPwPolicy } from "./lib/database/globalPwp.jsx"; +import { LocalPwPolicy } from "./lib/database/localPwp.jsx"; import { Modal, Icon, @@ -28,6 +30,8 @@ import "./css/ds.css"; const DB_CONFIG = "dbconfig"; const CHAINING_CONFIG = "chaining-config"; const BACKUP_CONFIG = "backups"; +const PWP_CONFIG = "pwpolicy"; +const LOCAL_PWP_CONFIG = "localpwpolicy"; const treeViewContainerStyles = { width: '295px', }; @@ -189,7 +193,6 @@ export class Database extends React.Component { importcacheauto: attrs['nsslapd-import-cache-autosize'], importcachesize: attrs['nsslapd-import-cachesize'], }, - loaded: true, configUpdated: 1 }), this.setState({configUpdated: 0})); }) @@ -360,18 +363,38 @@ export class Database extends React.Component { id: "backups", }, { - "text": "Suffixes", - "icon": "pficon-catalog", - "state": {"expanded": true}, + text: "Password Policies", + icon: "pficon-key", selectable: false, - "nodes": [] + state: {"expanded": true}, + "nodes": [ + { + text: "Global Policy", + icon: "glyphicon glyphicon-globe", + selectable: true, + id: "pwpolicy", + }, + { + text: "Local Policies", + icon: "pficon-home", + selectable: true, + id: "localpwpolicy", + }, + ] + }, + { + text: "Suffixes", + icon: "pficon-catalog", + state: {"expanded": true}, + selectable: false, + nodes: [] } ]; let current_node = this.state.node_name; if (fullReset) { current_node = DB_CONFIG; } - basicData[3].nodes = treeData; + basicData[4].nodes = treeData; this.setState(() => ({ nodes: basicData, @@ -463,6 +486,8 @@ export class Database extends React.Component { if (selectedNode.id == "dbconfig" || selectedNode.id == "chaining-config" || + selectedNode.id == "pwpolicy" || + selectedNode.id == "localpwpolicy" || selectedNode.id == "backups") { // Nothing special to do, these configurations have already been loaded this.setState(prevState => { @@ -557,8 +582,8 @@ export class Database extends React.Component { el.setAttribute('title', el.innerText); } this.setState({ - disableTree: false - }); + disableTree: false, + }, this.loadAttrs()); } showSuffixModal () { @@ -1070,17 +1095,18 @@ export class Database extends React.Component { "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", "schema", "attributetypes", "list" ]; - log_cmd("Suffixes", "Get attrs", attr_cmd); + log_cmd("loadAttrs", "Get attrs", attr_cmd); cockpit .spawn(attr_cmd, { superuser: true, err: "message" }) .done(content => { let attrContent = JSON.parse(content); let attrs = []; for (let content of attrContent['items']) { - attrs.push(content.name); + attrs.push(content.name[0]); } this.setState({ attributes: attrs, + loaded: true }); }) .fail(err => { @@ -1101,6 +1127,7 @@ export class Database extends React.Component { render() { const { nodes } = this.state; let db_element = ""; + let body = ""; let disabled = "tree-view-container"; if (this.state.disableTree) { disabled = "tree-view-container ds-disabled"; @@ -1127,6 +1154,22 @@ export class Database extends React.Component { enableTree={this.enableTree} key={this.state.chainingUpdated} />; + } else if (this.state.node_name == PWP_CONFIG) { + db_element = + ; + } else if (this.state.node_name == LOCAL_PWP_CONFIG) { + db_element = + ; } else if (this.state.node_name == BACKUP_CONFIG) { db_element = - + body =
@@ -1221,7 +1257,22 @@ export class Database extends React.Component {
{db_element}
-
+
; + } else { + body = +
+

Loading database configuration ...

+ +
; + } + + return ( +
+ + {body} No instances'); @@ -221,7 +220,6 @@ function check_inst_alive (connect_err) { $(".all-pages").hide(); $("#ds-navigation").show(); $("#server-content").show(); - $("#server-config").show(); } $("#not-running").hide(); $("#no-connect").hide(); @@ -378,9 +376,6 @@ function load_config (refresh){ progress = 10; update_progress(); - // Load the configuration for all the pages. - var dropdowns = ['local-pwp-suffix', 'select-repl-cfg-suffix']; - // Show the spinner, and reset the pages $("#loading-msg").html("Loading Directory Server configuration for " + server_id + "..."); $("#everything").hide(); @@ -395,22 +390,8 @@ function load_config (refresh){ var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','backend', 'suffix', 'list', '--suffix']; log_cmd('load_config', 'get backend list', cmd); cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) { - // Update dropdowns - for (var idx in dropdowns) { - $("#" + dropdowns[idx]).find('option').remove(); - } - var obj = JSON.parse(data); - for (var idx in obj['items']) { - for (var list in dropdowns){ - $("#" + dropdowns[list]).append(''); - } - } - - // Server page - get_and_set_config(); - get_and_set_sasl(); - get_and_set_localpwp(); update_progress(); + config_loaded = 1; // Initialize the tabs $(".ds-tab-list").css( 'color', '#777'); @@ -422,7 +403,6 @@ function load_config (refresh){ $("#loading-page").hide(); $("#everything").show(); $("#server-content").show(); - $("#server-config").show(); clearInterval(loading_config); loading_cfg = 0; @@ -444,6 +424,313 @@ function load_config (refresh){ }); } + + +// Create Instance +$("#create-inst-save").on("click", function() { + $(".ds-modal-error").hide(); + $(".ds-inst-input").css("border-color", "initial"); + + /* + * Validate settings and update the INF settings + */ + let setup_inf = create_inf_template; + + // Server ID + let new_server_id = $("#create-inst-serverid").val(); + if (new_server_id == ""){ + report_err($("#create-inst-serverid"), 'You must provide an Instance name'); + $("#create-inst-serverid").css("border-color", "red"); + return; + } else { + new_server_id = new_server_id.replace(/^slapd-/i, ""); // strip "slapd-" + if (new_server_id.length > 128) { + report_err($("#create-inst-serverid"), 'Instance name is too long, it must not exceed 128 characters'); + $("#create-inst-serverid").css("border-color", "red"); + return; + } + if (new_server_id.match(/^[#%:A-Za-z0-9_\-]+$/g)) { + setup_inf = setup_inf.replace('INST_NAME', new_server_id); + } else { + report_err($("#create-inst-serverid"), 'Instance name can only contain letters, numbers, and: # % : - _'); + $("#create-inst-serverid").css("border-color", "red"); + return; + } + } + + // Port + let server_port = $("#create-inst-port").val(); + if (server_port == ""){ + report_err($("#create-inst-port"), 'You must provide a port number'); + $("#create-inst-port").css("border-color", "red"); + return; + } else if (!valid_port(server_port)) { + report_err($("#create-inst-port"), 'Port must be a number between 1 and 65534!'); + $("#create-inst-port").css("border-color", "red"); + return; + } else { + setup_inf = setup_inf.replace('PORT', server_port); + } + + // Secure Port + let secure_port = $("#create-inst-secureport").val(); + if (secure_port == ""){ + report_err($("#create-inst-secureport"), 'You must provide a secure port number'); + $("#create-inst-secureport").css("border-color", "red"); + return; + } else if (!valid_port(secure_port)) { + report_err($("#create-inst-secureport"), 'Secure port must be a number!'); + $("#create-inst-secureport").css("border-color", "red"); + return; + } else { + setup_inf = setup_inf.replace('SECURE_PORT', secure_port); + } + + // Root DN + let server_rootdn = $("#create-inst-rootdn").val(); + if (server_rootdn == ""){ + report_err($("#create-inst-rootdn"), 'You must provide a Directory Manager DN'); + $("#create-inst-rootdn").css("border-color", "red"); + return; + } else { + setup_inf = setup_inf.replace('ROOTDN', server_rootdn); + } + + // Setup Self-Signed Certs + if ( $("#create-inst-tls").is(":checked") ){ + setup_inf = setup_inf.replace('SELF_SIGN', 'True'); + } else { + setup_inf = setup_inf.replace('SELF_SIGN', 'False'); + } + + // Root DN password + let root_pw = $("#rootdn-pw").val(); + let root_pw_confirm = $("#rootdn-pw-confirm").val(); + if (root_pw != root_pw_confirm) { + report_err($("#rootdn-pw"), 'Directory Manager passwords do not match!'); + $("#rootdn-pw-confirm").css("border-color", "red"); + return; + } else if (root_pw == ""){ + report_err($("#rootdn-pw"), 'Directory Manager password can not be empty!'); + $("#rootdn-pw-confirm").css("border-color", "red"); + return; + } else if (root_pw.length < 8) { + report_err($("#rootdn-pw"), 'Directory Manager password must have at least 8 characters'); + $("#rootdn-pw-confirm").css("border-color", "red"); + return; + } else { + setup_inf = setup_inf.replace('ROOTPW', root_pw); + } + + // Backend/Suffix + let backend_name = $("#backend-name").val(); + let backend_suffix = $("#backend-suffix").val(); + if ( (backend_name != "" && backend_suffix == "") || (backend_name == "" && backend_suffix != "") ) { + if (backend_name == ""){ + report_err($("#backend-name"), 'If you specify a backend suffix, you must also specify a backend name'); + $("#backend-name").css("border-color", "red"); + return; + } else { + report_err($("#backend-suffix"), 'If you specify a backend name, you must also specify a backend suffix'); + $("#backend-suffix").css("border-color", "red"); + return; + } + } + if (backend_name != ""){ + // We definitely have a backend name and suffix, next validate the suffix is a DN + if (valid_dn(backend_suffix)) { + // It's valid, add it + setup_inf += "\n[backend-" + backend_name + "]\nsuffix = " + backend_suffix + "\n"; + } else { + // Not a valid DN + report_err($("#backend-suffix"), 'Invalid DN for Backend Suffix'); + return; + } + if ( $("#create-sample-entries").is(":checked") ) { + setup_inf += '\nsample_entries = yes\n'; + } else if ( $("#create-suffix-entry").is(":checked") ) { + setup_inf += '\ncreate_suffix_entry = yes\n'; + } + } + + /* + * Here are steps we take to create the instance + * + * [1] Get FQDN Name for nsslapd-localhost setting in setup file + * [2] Create a file for the inf setup parameters + * [3] Set strict permissions on that file + * [4] Populate the new setup file with settings (including cleartext password) + * [5] Create the instance + * [6] Remove setup file + */ + cockpit.spawn(["hostname", "--fqdn"], { superuser: true, "err": "message" }).fail(function(ex, data) { + // Failed to get FQDN + popup_err("Failed to get hostname!", data); + }).done(function (data) { + /* + * We have FQDN, so set the hostname in inf file, and create the setup file + */ + setup_inf = setup_inf.replace('FQDN', data); + let setup_file = "/tmp/389-setup-" + (new Date).getTime() + ".inf"; + let rm_cmd = ['rm', setup_file]; + let create_file_cmd = ['touch', setup_file]; + cockpit.spawn(create_file_cmd, { superuser: true, "err": "message" }).fail(function(ex, data) { + // Failed to create setup file + popup_err("Failed to create installation file!", data); + }).done(function (){ + /* + * We have our new setup file, now set permissions on that setup file before we add sensitive data + */ + let chmod_cmd = ['chmod', '600', setup_file]; + cockpit.spawn(chmod_cmd, { superuser: true, "err": "message" }).fail(function(ex, data) { + // Failed to set permissions on setup file + cockpit.spawn(rm_cmd, { superuser: true }); // Remove Inf file with clear text password + $("#create-inst-spinner").hide(); + popup_err("Failed to set permission on setup file " + setup_file + ": ", data); + }).done(function () { + /* + * Success we have our setup file and it has the correct permissions. + * Now populate the setup file... + */ + let cmd = ["/bin/sh", "-c", '/usr/bin/echo -e "' + setup_inf + '" >> ' + setup_file]; + cockpit.spawn(cmd, { superuser: true, "err": "message" }).fail(function(ex, data) { + // Failed to populate setup file + popup_err("Failed to populate installation file!", data); + }).done(function (){ + /* + * Next, create the instance... + */ + let cmd = [DSCREATE, 'from-file', setup_file]; + cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV] }).fail(function(ex, data) { + // Failed to create the new instance! + cockpit.spawn(rm_cmd, { superuser: true }); // Remove Inf file with clear text password + $("#create-inst-spinner").hide(); + popup_err("Failed to create instance!", data); + }).done(function (){ + // Success!!! Now cleanup everything up... + cockpit.spawn(rm_cmd, { superuser: true }); // Remove Inf file with clear text password + $("#create-inst-spinner").hide(); + $("#server-list-menu").attr('disabled', false); + $("#no-instances").hide(); + get_insts(); // Refresh server list + popup_success("Successfully created instance: slapd-" + new_server_id + ""); + $("#create-inst-form").modal('toggle'); + }); + }); + $("#create-inst-spinner").show(); + }); + }); + }).fail(function(data) { + console.log("failed: " + data.message); + }); +}); + +var create_full_template = + "[general]\n" + + "config_version = 2\n" + + "defaults = 999999999\n" + + "full_machine_name = FQDN\n" + + "selinux = True\n" + + "strict_host_checking = True\n" + + "systemd = True\n" + + "[slapd]\n" + + "backup_dir = /var/lib/dirsrv/slapd-{instance_name}/bak\n" + + "bin_dir = /usr/bin\n" + + "cert_dir = /etc/dirsrv/slapd-{instance_name}\n" + + "config_dir = /etc/dirsrv/slapd-{instance_name}\n" + + "data_dir = /usr/share\n" + + "db_dir = /var/lib/dirsrv/slapd-{instance_name}/db\n" + + "user = dirsrv\n" + + "group = dirsrv\n" + + "initconfig_dir = /etc/sysconfig\n" + + "inst_dir = /usr/lib64/dirsrv/slapd-{instance_name}\n" + + "instance_name = localhost\n" + + "ldif_dir = /var/lib/dirsrv/slapd-{instance_name}/ldif\n" + + "lib_dir = /usr/lib64\n" + + "local_state_dir = /var\n" + + "lock_dir = /var/lock/dirsrv/slapd-{instance_name}\n" + + "log_dir = /var/log/dirsrv/slapd-{instance_name}\n" + + "port = PORT\n" + + "prefix = /usr\n" + + "root_dn = ROOTDN\n" + + "root_password = ROOTPW\n" + + "run_dir = /var/run/dirsrv\n" + + "sbin_dir = /usr/sbin\n" + + "schema_dir = /etc/dirsrv/slapd-{instance_name}/schema\n" + + "secure_port = SECURE_PORT\n" + + "self_sign_cert = True\n" + + "sysconf_dir = /etc\n" + + "tmp_dir = /tmp\n"; + +var create_inf_template = + "[general]\n" + + "config_version = 2\n" + + "full_machine_name = FQDN\n\n" + + "[slapd]\n" + + "user = dirsrv\n" + + "group = dirsrv\n" + + "instance_name = INST_NAME\n" + + "port = PORT\n" + + "root_dn = ROOTDN\n" + + "root_password = ROOTPW\n" + + "secure_port = SECURE_PORT\n" + + "self_sign_cert = SELF_SIGN\n"; + + +function clear_inst_form() { + $(".ds-modal-error").hide(); + $("#create-inst-serverid").val(""); + $("#create-inst-port").val("389"); + $("#create-inst-secureport").val("636"); + $("#create-inst-rootdn").val("cn=Directory Manager"); + $("#rootdn-pw").val(""); + $("#rootdn-pw-confirm").val(""); + $("#backend-suffix").val("dc=example,dc=com"); + $("#backend-name").val("userRoot"); + $("#create-sample-entries").prop('checked', false); + $("#create-inst-tls").prop('checked', true); + $(".ds-inst-input").css("border-color", "initial"); +} + +function do_backup(server_inst, backup_name) { + var cmd = [DSCTL, '-j', server_inst, 'status']; + $("#backup-spinner").show(); + cockpit.spawn(cmd, { superuser: true}). + done(function(status_data) { + var status_json = JSON.parse(status_data); + if (status_json.running == true) { + 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) { + $("#backup-spinner").hide(); + popup_success("Backup has been created"); + $("#backup-form").modal('toggle'); + }). + fail(function(data) { + $("#backup-spinner").hide(); + popup_err("Failed to backup the server", data.message); + }) + } else { + var cmd = [DSCTL, server_inst, 'db2bak', backup_name]; + log_cmd('#ds-backup-btn (click)', 'Backup server instance (offline)', cmd); + cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}). + done(function(data) { + $("#backup-spinner").hide(); + popup_success("Backup has been created"); + $("#backup-form").modal('toggle'); + }). + fail(function(data) { + $("#backup-spinner").hide(); + popup_err("Failed to backup the server", data.message); + }); + } + }). + fail(function() { + popup_err("Failed to check the server status", data.message); + }); +} + $(window.document).ready(function() { if(navigator.userAgent.toLowerCase().indexOf('firefoxf') > -1) { $("select@@@").focus( function() { @@ -452,6 +739,134 @@ $(window.document).ready(function() { this.style.setProperty( 'text-shadow', '0 0 0 #000', 'important' ); }); } + + // Set an interval event to wait for all the pages to load, then load the config + var init_config = setInterval(function() { + /* + * Stop, Start, and Restart server + */ + + let banner = document.getElementById("start-server-btn"); + if (banner == null) { + // Not ready yet, return and try again + return; + } + + get_insts(); + + /* Restore. load restore table with current backups */ + document.getElementById("restore-server-btn").addEventListener("click", function() { + var cmd = [DSCTL, '-j', server_inst, 'backups']; + log_cmd('#restore-server-btn (click)', 'Restore server instance', cmd); + cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) { + let backup_btn = ""; + let del_btn = ""; + let obj = JSON.parse(data); + backup_table.clear().draw( false ); + for (var i = 0; i < obj.items.length; i++) { + let backup_name = obj.items[i][0]; + let backup_date = obj.items[i][1]; + let backup_size = obj.items[i][2]; + backup_table.row.add([backup_name, backup_date, backup_size, backup_btn, del_btn]).draw( false ); + } + }).fail(function(data) { + popup_err("Failed to get list of backups", data.message); + }); + }); + + document.getElementById("backup-server-btn").addEventListener("click", function() { + $("#backup-name").val(""); + }); + + document.getElementById("start-server-btn").addEventListener("click", function() { + $("#ds-start-inst").html(" Starting instance " + server_id + "..."); + $("#start-instance-form").modal('toggle'); + var cmd = [DSCTL, server_inst, 'start']; + 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(true); + popup_success("Started instance \"" + server_id + "\""); + }).fail(function(data) { + $("#start-instance-form").modal('toggle'); + popup_err("Failed to start instance \"" + server_id, data.message); + }); + }); + + document.getElementById("stop-server-btn").addEventListener("click", function() { + $("#ds-stop-inst").html(" Stopping instance " + server_id + "..."); + $("#stop-instance-form").modal('toggle'); + var cmd = [DSCTL, server_inst, 'stop']; + log_cmd('#stop-server-btn (click)', 'Stop server instance', cmd); + cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) { + $("#stop-instance-form").modal('toggle'); + popup_success("Stopped instance \"" + server_id + "\""); + check_inst_alive(); + }).fail(function(data) { + $("#stop-instance-form").modal('toggle'); + popup_err("Error", "Failed to stop instance \"" + server_id+ "\"", data.message); + check_inst_alive(); + }); + }); + + + document.getElementById("restart-server-btn").addEventListener("click", function() { + $("#ds-restart-inst").html(" Restarting instance " + server_id + "..."); + $("#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(true); + popup_success("Restarted instance \"" + server_id + "\""); + }).fail(function(data) { + $("#restart-instance-form").modal('toggle'); + popup_err("Failed to restart instance \"" + server_id + "\"", data.message); + }); + }); + + document.getElementById("remove-server-btn").addEventListener("click", function() { + popup_confirm("Are you sure you want to this remove instance: " + server_id + "", "Confirmation", function (yes) { + if (yes) { + var cmd = [DSCTL, server_inst, "remove", "--do-it"]; + $("#ds-remove-inst").html(" Removing instance " + server_id + "..."); + $("#remove-instance-form").modal('toggle'); + log_cmd('#remove-server-btn (click)', 'Remove instance', cmd); + cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) { + $("#remove-instance-form").modal('toggle'); + popup_success("Instance has been deleted"); + get_insts(); + }).fail(function(data) { + $("#remove-instance-form").modal('toggle'); + popup_err("Failed to remove instance", data.message); + }); + } + }); + }); + clearInterval(init_config); + }, 250); + + $("#main-banner").load("banner.html"); + check_for_389(); + + $("#server-tab").css( 'color', '#228bc0'); // Set first tab as highlighted + + // Events + $(".ds-nav-choice").on('click', function (){ + // This highlights each nav tab when clicked + $(".ds-tab-list").css( 'color', '#777'); + var tab = $(this).attr("parent-id"); + $("#" + tab).css( 'color', '#228bc0'); + }); + + $("#server-tasks-btn").on("click", function() { + $(".all-pages").hide(); + $("#server-tasks").show(); + }); + $("#server-tab").on("click", function() { + $(".all-pages").hide(); + $("#server-content").show(); + }); $("#plugin-tab").on("click", function() { $(".all-pages").hide(); $("#plugin-content").show(); @@ -464,10 +879,6 @@ $(window.document).ready(function() { $(".all-pages").hide(); $("#monitor-content").show(); }); - $("#security-tab").on("click", function() { - $(".all-pages").hide(); - $("#security-content").show(); - }); $("#schema-tab").on("click", function() { $(".all-pages").hide(); $("#schema-content").show(); @@ -476,4 +887,188 @@ $(window.document).ready(function() { $(".all-pages").hide(); $("#replication-content").show(); }); + + // Create instance form + $("#create-server-btn").on("click", function() { + clear_inst_form(); + set_ports(); + }); + $("#no-inst-create-btn").on("click", function () { + clear_inst_form(); + }); + + // backup/restore table + var backup_table = $('#backup-table').DataTable( { + "paging": true, + "bAutoWidth": false, + "dom": '<"pull-left"f><"pull-right"l>tip', + "lengthMenu": [ 10, 25, 50, 100], + "language": { + "emptyTable": "No backups available for restore", + "search": "Search Backups" + }, + "columnDefs": [ { + "targets": [3, 4], + "orderable": false + } ], + "columns": [ + { "width": "120px" }, + { "width": "80px" }, + { "width": "30px" }, + { "width": "40px" }, + { "width": "30px" } + ], + }); + + $(".all-pages").hide(); + $("#server-content").show(); + + // To remove text border on firefox on dropdowns) + if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { + $("select").focus( function() { + this.style.setProperty( 'outline', 'none', 'important' ); + this.style.setProperty( 'color', 'rgba(0,0,0,0)', 'important' ); + this.style.setProperty( 'text-shadow', '0 0 0 #000', 'important' ); + }); + } + + $(".ds-tab-standalone").on('click', function (){ + $(".ds-tab-list").css( 'color', '#777'); + $(this).css( 'color', '#228bc0'); + }); + + /* Backup server */ + $("#ds-backup-btn").on('click', function () { + var backup_name = $("#backup-name").val(); + if (backup_name == ""){ + popup_msg("Error", "Backup must have a name"); + return; + } + if (backup_name.indexOf(' ') >= 0) { + popup_msg("Error", "Backup name can not contain any spaces"); + return; + } + if (backup_name.indexOf('/') >= 0) { + popup_msg("Error", "Backup name can not contain a forward slash. " + + "Backups are written to the server's backup directory (nsslapd-bakdir)"); + return; + } + + // First check if backup name is already used + var check_cmd = [DSCTL, '-j', server_inst, 'backups']; + log_cmd('#ds-backup-btn (click)', 'Restore server instance', check_cmd); + cockpit.spawn(check_cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) { + var obj = JSON.parse(data); + var found_backup = false; + for (var i = 0; i < obj.items.length; i++) { + if (obj.items[i][0] == backup_name) { + found_backup = true; + break; + } + } + if (found_backup) { + popup_confirm("A backup already exists with this name, replace it?", "Confirmation", function (yes) { + if (yes) { + do_backup(server_inst, backup_name); + } else { + return; + } + }); + } else { + do_backup(server_inst, backup_name); + } + }); + }); + + /* Restore server */ + $(document).on('click', '.restore-btn', function(e) { + e.preventDefault(); + var data = backup_table.row( $(this).parents('tr') ).data(); + var restore_name = data[0]; + popup_confirm("Are you sure you want to restore this backup: " + restore_name + "", "Confirmation", function (yes) { + if (yes) { + var cmd = [DSCTL, '-j', server_inst, 'status']; + $("#restore-spinner").show(); + cockpit.spawn(cmd, { superuser: true}). + done(function(status_data) { + var status_json = JSON.parse(status_data); + if (status_json.running == true) { + var cmd = [DSCONF, server_inst, 'backup', 'restore', restore_name]; + log_cmd('.restore-btn (click)', 'Restore server instance(online)', cmd); + cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}). + done(function(data) { + $("#restore-spinner").hide(); + popup_success("The backup has been restored"); + $("#restore-form").modal('toggle'); + }). + fail(function(data) { + $("#restore-spinner").hide(); + popup_err("Failed to restore from the backup", data.message); + }); + } else { + var cmd = [DSCTL, server_inst, 'bak2db', restore_name]; + log_cmd('.restore-btn (click)', 'Restore server instance(offline)', cmd); + cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}). + done(function(data) { + $("#restore-spinner").hide(); + popup_success("The backup has been restored"); + $("#restore-form").modal('toggle'); + }). + fail(function(data) { + $("#restore-spinner").hide(); + popup_err("Failed to restore from the backup", data.message); + }); + } + }). + fail(function() { + popup_err("Failed to check the server status", data.message); + }); + } + }); + }); + + /* Delete backup directory */ + $(document).on('click', '.ds-del-backup-btn', function(e) { + e.preventDefault(); + var data = backup_table.row( $(this).parents('tr') ).data(); + var restore_name = data[0]; + var backup_row = $(this); + popup_confirm("Are you sure you want to delete this backup: " + restore_name + "", "Confirmation", function (yes) { + if (yes) { + var cmd = [DSCTL, server_inst, 'backups', '--delete', restore_name]; + $("#restore-spinner").show(); + log_cmd('.ds-del-backup-btn (click)', 'Delete backup', cmd); + cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) { + $("#restore-spinner").hide(); + backup_table.row( backup_row.parents('tr') ).remove().draw( false ); + popup_success("The backup has been deleted"); + }).fail(function(data) { + $("#restore-spinner").hide(); + popup_err("Failed to delete the backup", data.message); + }); + } + }); + }); + + /* reload schema */ + $("#schema-reload-btn").on("click", function () { + var schema_dir = $("#reload-dir").val(); + if (schema_dir != ""){ + var cmd = [DSCONF, server_inst, 'schema', 'reload', '--schemadir', schema_dir, '--wait']; + } else { + var cmd = [DSCONF, server_inst, 'schema', 'reload', '--wait']; + } + $("#reload-spinner").show(); + log_cmd('#schema-reload-btn (click)', 'Reload schema files', cmd); + cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) { + popup_success("Successfully reloaded schema"); // TODO use timed interval success msg (waiting for another PR top be merged before we can add it) + $("#schema-reload-form").modal('toggle'); + $("#reload-spinner").hide(); + }).fail(function(data) { + popup_err("Failed to reload schema files", data.message); + $("#reload-spinner").hide(); + }); + }); + + }); diff --git a/src/cockpit/389-console/src/index.es6 b/src/cockpit/389-console/src/index.es6 index 6f2673f..9344ffa 100644 --- a/src/cockpit/389-console/src/index.es6 +++ b/src/cockpit/389-console/src/index.es6 @@ -4,8 +4,8 @@ import { Plugins } from "./plugins.jsx"; import { Database } from "./database.jsx"; import { Monitor } from "./monitor.jsx"; import { Schema } from "./schema.jsx"; -import { Security } from "./security.jsx"; import { Replication } from "./replication.jsx"; +import { Server } from "./server.jsx"; var serverIdElem; @@ -21,6 +21,11 @@ function renderReactDOM(clear) { let d = new Date(); let tabKey = d.getTime(); + // Server Tab + ReactDOM.render( + , + document.getElementById("server") + ); // Plugins Tab ReactDOM.render( , @@ -45,12 +50,6 @@ function renderReactDOM(clear) { document.getElementById("monitor") ); - // Security tab - ReactDOM.render( - , - document.getElementById("security") - ); - // Schema tab ReactDOM.render( , diff --git a/src/cockpit/389-console/src/index.html b/src/cockpit/389-console/src/index.html index c1b9952..1df2d19 100644 --- a/src/cockpit/389-console/src/index.html +++ b/src/cockpit/389-console/src/index.html @@ -18,7 +18,6 @@ - @@ -56,39 +55,9 @@
- - -