From c013a0287a8d85f082e1f21050d4b95ab6ca89ce Mon Sep 17 00:00:00 2001 From: Simon Pichugin Date: Mar 02 2020 18:34:21 +0000 Subject: Issue 50904 - Connect All React Components And Refactor the Main Navigation Tab Code Description: Port the rest of ds.js and index.html code to React. It includes instance navigation, creation, removal, and other dsctl tasks. Fix https://pagure.io/389-ds-base/issue/49902 https://pagure.io/389-ds-base/issue/50904 Reviewed by: mreynolds (Thanks!) --- diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css index 82b1ab3..76bf2a9 100644 --- a/src/cockpit/389-console/src/css/ds.css +++ b/src/cockpit/389-console/src/css/ds.css @@ -59,12 +59,6 @@ margin-top: 2px; } -.ds-refresh:hover { - color: DarkGray; - background-color: white; - background-image: none; -} - .dataTables_wrapper { padding: 0px !important; margin-bottom: 10px !important; @@ -282,6 +276,16 @@ td { padding: 5px; } +.ds-operate-spinner { + margin-left: 10px; + top: 10px; +} + +.ds-tab-main { + margin-left: 2%; + font-size: 18px; +} + .ds-config-label { margin-top: 10px; width: 225px !important; @@ -513,7 +517,6 @@ option { text-align: left; } -.ds-accordion:hover, .ds-accordion:focus { background-color: transparent !important; outline: none !important; @@ -523,10 +526,6 @@ option { box-shadow: 0 !important; } -.ds-accordion:active { - background-color: transparent !important; -} - .ds-margin-top { margin-top: 10px !important; } @@ -610,10 +609,6 @@ option { width: 100%; } -.ds-inst-indent { - margin-left: 240px; -} - .ds-left-margin { margin-left: 10px !important; } @@ -679,7 +674,7 @@ option { .ds-loading { position: fixed; top: 25%; - left: 35%; + left: 50%; transform: translate(-25%, -35%); } @@ -855,12 +850,6 @@ option { display: block; } -.treeview-hover .treeitem-row:hover { - background-color: #def3ff; - border-color: #bee1f4; - display: block; -} - input { padding-left: 5px !important; } diff --git a/src/cockpit/389-console/src/database.jsx b/src/cockpit/389-console/src/database.jsx index 3ac1e7a..efa3ce6 100644 --- a/src/cockpit/389-console/src/database.jsx +++ b/src/cockpit/389-console/src/database.jsx @@ -1,6 +1,5 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController } from "./lib/notifications.jsx"; import { log_cmd } from "./lib/tools.jsx"; import { ChainingConfig, @@ -40,7 +39,7 @@ export class Database extends React.Component { constructor(props) { super(props); this.state = { - notifications: [], + firstLoad: true, errObj: {}, nodes: [], node_name: "", @@ -76,8 +75,6 @@ export class Database extends React.Component { // General this.selectNode = this.selectNode.bind(this); - this.removeNotification = this.removeNotification.bind(this); - this.addNotification = this.addNotification.bind(this); this.handleChange = this.handleChange.bind(this); this.handleRadioChange = this.handleRadioChange.bind(this); this.loadGlobalConfig = this.loadGlobalConfig.bind(this); @@ -109,23 +106,22 @@ export class Database extends React.Component { this.enableTree = this.enableTree.bind(this); } - componentWillMount () { - if (!this.state.loaded) { - this.loadGlobalConfig(); - this.loadChainingConfig(); - this.loadLDIFs(); - this.loadBackups(); - this.loadSuffixList(); - } - } - - componentDidMount() { - this.loadSuffixTree(false); - } - componentDidUpdate(prevProps) { - if (this.props.serverId !== prevProps.serverId) { - this.loadSuffixTree(false); + if (this.props.wasActiveList.includes(2)) { + if (this.state.firstLoad) { + if (!this.state.loaded) { + this.loadGlobalConfig(); + this.loadChainingConfig(); + this.loadLDIFs(); + this.loadBackups(); + this.loadSuffixList(); + } + this.loadSuffixTree(false); + } else { + if (this.props.serverId !== prevProps.serverId) { + this.loadSuffixTree(false); + } + } } } @@ -146,6 +142,9 @@ export class Database extends React.Component { } loadGlobalConfig () { + if (this.state.firstLoad) { + this.setState({ firstLoad: false }); + } const cmd = [ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", "backend", "config", "get" @@ -198,7 +197,7 @@ export class Database extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading database configuration - ${errMsg.desc}` ); @@ -285,7 +284,7 @@ export class Database extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading default chaining configuration - ${errMsg.desc}` ); @@ -469,7 +468,7 @@ export class Database extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error getting chaining link configuration - ${errMsg.desc}` ); @@ -551,29 +550,6 @@ export class Database extends React.Component { }); } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - update_tree_nodes() { // Set title to the text value of each suffix node. We need to do this // so we can read long suffixes in the UI tree div @@ -644,7 +620,7 @@ export class Database extends React.Component { }; if (this.state.createSuffix == "") { - this.addNotification( + this.props.addNotification( "warning", `Missing the suffix DN` ); @@ -652,7 +628,7 @@ export class Database extends React.Component { errors = true; } if (this.state.createBeName == "") { - this.addNotification( + this.props.addNotification( "warning", `Missing the suffix backend name` ); @@ -683,7 +659,7 @@ export class Database extends React.Component { .spawn(cmd, { superuser: true, err: "message" }) .done(content => { this.closeSuffixModal(); - this.addNotification( + this.props.addNotification( "success", `Successfully create new suffix` ); @@ -693,7 +669,7 @@ export class Database extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error creating suffix - ${errMsg.desc}` ); @@ -853,7 +829,7 @@ export class Database extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading indexes for ${suffix} - ${errMsg.desc}` ); @@ -1008,7 +984,7 @@ export class Database extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading indexes for ${suffix} - ${errMsg.desc}` ); @@ -1019,7 +995,7 @@ export class Database extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error attribute encryption for ${suffix} - ${errMsg.desc}` ); @@ -1030,7 +1006,7 @@ export class Database extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading VLV indexes for ${suffix} - ${errMsg.desc}` ); @@ -1041,7 +1017,7 @@ export class Database extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading config for ${suffix} - ${errMsg.desc}` ); @@ -1074,7 +1050,7 @@ export class Database extends React.Component { const cmd = [ "dsctl", "-j", this.props.serverId, "backups" ]; - log_cmd("loadBackups", "Load Backups", cmd); + log_cmd("loadBackupsDatabase", "Load Backups", cmd); cockpit .spawn(cmd, { superuser: true, err: "message" }) .done(content => { @@ -1111,7 +1087,7 @@ export class Database extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Failed to get attributes - ${errMsg.desc}` ); @@ -1138,7 +1114,7 @@ export class Database extends React.Component { db_element = ; @@ -1166,7 +1142,7 @@ export class Database extends React.Component { db_element = ; @@ -1174,7 +1150,7 @@ export class Database extends React.Component { db_element = - {body} " + cmd_list.join(' ')); - } -} - -// TODO validation functions - -function valid_dn (dn){ - // Validate value is a valid DN (sanity validation) - var result = dn_regex.test(dn); - return result; -} - -function valid_num (val){ - // Validate value is a number - let result = !isNaN(val); - return result; -} - -function valid_port (val){ - // Validate value is a number and between 1 and 65535 - let result = !isNaN(val); - if (result) { - if (val < 1 || val > 65535) { - result = false; - } - } - return result; -} - -function tableize (val) { - // Truncate a long value to fit inside table - if (val.length > 25){ - val = val.substring(0,25) + "..."; - } - return val; -} - -/* - * Set the ports numbers on the instance creation form. If the default ports - * are taken just unset the values. - */ -function set_ports() { - var cmd = ['ss', '-ntpl']; - cockpit.spawn(cmd, { superuser: true, "err": "message"}).done(function(data) { - var lines = data.split('\n'); - $("#create-inst-port").val("389"); - $("#create-inst-secureport").val("636"); - for (var i = 0; i < lines.length; i++){ - if (lines[i].indexOf("LISTEN") != -1 && lines[i].indexOf(":389 ") != -1) { - $("#create-inst-port").val(""); - } - if (lines[i].indexOf("LISTEN") != -1 && lines[i].indexOf(":636 ") != -1) { - $("#create-inst-secureport").val(""); - } - } - }); -} - -function sort_list (sel) { - var opts_list = sel.find('option'); - opts_list.sort(function(a, b) { return $(a).text() > $(b).text() ? 1 : -1; }); - sel.html('').append(opts_list); -} - - -function get_date_string (timestamp) { - // Convert DS timestamp to a friendly string: 20180921142257Z -> 10/21/2018, 2:22:57 PM - let year = timestamp.substr(0,4); - let month = timestamp.substr(4,2); - let day = timestamp.substr(6,2); - let hour = timestamp.substr(8,2); - let minute = timestamp.substr(10,2); - let sec = timestamp.substr(12,2); - let date = new Date(parseInt(year), parseInt(month), parseInt(day), - parseInt(hour), parseInt(minute), parseInt(sec)); - - return date.toLocaleString(); -} - -function get_date_diff(start, end) { - // Get the start up date - let year = start.substr(0,4); - let month = start.substr(4,2); - let day = start.substr(6,2); - let hour = start.substr(8,2); - let minute = start.substr(10,2); - let sec = start.substr(12,2); - let startDate = new Date(parseInt(year), parseInt(month), parseInt(day), - parseInt(hour), parseInt(minute), parseInt(sec)); - - // Get the servers current date - year = end.substr(0,4); - month = end.substr(4,2); - day = end.substr(6,2); - hour = end.substr(8,2); - minute = end.substr(10,2); - sec = end.substr(12,2); - let currDate = new Date(parseInt(year), parseInt(month), parseInt(day), - parseInt(hour), parseInt(minute), parseInt(sec)); - - // Generate pretty elapsed time string - let seconds = Math.floor((startDate - (currDate))/1000); - let minutes = Math.floor(seconds/60); - let hours = Math.floor(minutes/60); - let days = Math.floor(hours/24); - hours = hours-(days*24); - minutes = minutes-(days*24*60)-(hours*60); - seconds = seconds-(days*24*60*60)-(hours*60*60)-(minutes*60); - - return("${days} days, ${hours} hours, ${minutes} minutes, and ${seconds} seconds"); -} - -function set_no_insts () { - $("#select-server").empty(); - $("#select-server").append(''); - $("#select-server select").val('No instances'); - - server_id = ""; - server_inst = ""; - - $("#server-list-menu").attr('disabled', true); - $("#ds-navigation").hide(); - $(".all-pages").hide(); - $("#no-instances").show(); -} - -function check_for_389 () { - var cmd = ["rpm", "-q", "389-ds-base"]; - - cockpit.spawn(cmd, { superuser: true }).fail(function(data) { - $("#server-list-menu").attr('disabled', true); - $("#ds-navigation").hide(); - $(".all-pages").hide(); - $("#no-package").show(); - }); -} - -function check_inst_alive (connect_err) { - // Check if this instance is started, if not hide configuration pages - if (connect_err === undefined) { - connect_err = 0; - } - cmd = [DSCTL, '-j', server_inst, 'status']; - cockpit.spawn(cmd, { superuser: true}). - done(function(status_data) { - var status_json = JSON.parse(status_data); - if (status_json.running == true) { - if (connect_err) { - $("#ds-navigation").hide(); - $(".all-pages").hide(); - $("#no-connect").show(); - } else { - // if nav page was hidden reset everything - if ($("#ds-navigation").is(":hidden") ){ - $(".all-pages").hide(); - $("#ds-navigation").show(); - $("#server-content").show(); - } - $("#not-running").hide(); - $("#no-connect").hide(); - } - } else { - $("#loading-page").hide(); - $("#ds-navigation").hide(); - $(".all-pages").hide(); - $("#not-running").show(); - } - }).fail(function(data) { - $("#loading-page").hide(); - $("#ds-navigation").hide(); - $(".all-pages").hide(); - $("#not-running").show(); - }); -} - -function get_insts() { - // Load initial forms - $("#server-list-menu").attr('disabled', false); - $("#ds-navigation").show(); - $(".all-pages").hide(); - $("#no-instances").hide(); - - var insts = []; - var cmd = ["/bin/sh", "-c", "/usr/bin/ls -d " + DS_HOME + "slapd-*"]; - cockpit.spawn(cmd, { superuser: true }).done(function(data) { - // Parse the output, and skip removed instances and empty lines - var lines = data.split('\n'); - var i = 0; - for (i = 0; i < lines.length; i++) { - if (lines[i].endsWith(".removed") == false && lines[i] != "") { - var serverid = lines[i].replace(DS_HOME, ""); - insts.push(serverid); - } - } - - if (server_id != "None") { - $("#ds-banner").html("Managing Instance "); - } - - // Populate the server instance drop down - $("#select-server").empty(); - for (i = 0; i < insts.length; i++) { - $("#select-server").append(''); - $("#select-server select").val(insts[i]); - } - - // Handle changing instance here - document.getElementById("select-server").addEventListener("change", function() { - server_id = $(this).val(); - server_inst = server_id.replace("slapd-", ""); - load_config(); - }); - - if (insts[0] === undefined) { - set_no_insts(); - $("#loading-page").hide(); - $("#everything").show(); - } else { - // We have at least one instance, make sure we "open" the UI - server_id = insts[0]; - server_inst = insts[0].replace("slapd-", ""); - check_inst_alive(); - // We have to dispatch an event for the React components rerender - // It should also trigger the listener defined before - server_select_elem = document.getElementById('select-server'); - server_select_elem.dispatchEvent(new Event('change')); - } - }).fail(function(error){ - set_no_insts(); - $("#loading-page").hide(); - $("#everything").show(); - }); -} - -function report_err( input, msg) { - $(".ds-modal-error").html('Error: ' + msg); - input.css("border-color", "red"); - $(".ds-modal-error").show(); -} - - -function popup_err(title, msg) { - // Display errors from the cli (we have to use pre tags) - bootpopup({ - title: title, - content: [ - '
' + msg + '
' - ] - }); - check_inst_alive(0); -} - -function popup_msg(title, msg) { - bootpopup({ - title: title, - content: [ - '

' + msg + '

' - ] - }); -} - -function popup_confirm(msg, title, callback) { - if(typeof callback !== "function") { - callback = function() {}; - } - var answer = false; - return bootpopup({ - title: title, - content: [ - msg - ], - showclose: false, - buttons: ["no", "yes"], - yes: function() { answer = true; }, - dismiss: function() { callback(answer); }, - }); -} - -function popup_success(msg) { - $('#success-msg').html(msg); - $('#success-form').modal('show'); - setTimeout(function() {$('#success-form').modal('hide');}, 2000); -} - -// This is called when any Save button is clicked on the main page. We call -// all the save functions for all the pages here. This is not used for modal forms -function save_all () { - save_config(); // Server Config Page -} - -var progress = 10; - -function update_progress () { - progress += 10; - if (progress > 100) { - progress = 100; - } - $("#ds-progress-label").text(progress + "%"); - $("#ds-progress-bar").attr("aria-valuenow", progress); - $("#ds-progress-bar").css("width", progress + "%"); -} - -var loading_cfg = 0; - -function load_config (refresh){ - // If we are currently loading config don't do it twice - if (loading_cfg == 1){ - return; - } - loading_cfg = 1; - progress = 10; - update_progress(); - - // Show the spinner, and reset the pages - $("#loading-msg").html("Loading Directory Server configuration for " + server_id + "..."); - $("#everything").hide(); - $(".all-pages").hide(); - $("#loading-page").show(); - config_loaded = 0; - - /* - * Start with the dropdowns, if this fails we stop here, otherwise we assume - * we are up and running and we can load the other config/ - */ - 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_progress(); - config_loaded = 1; - - // Initialize the tabs - $(".ds-tab-list").css( 'color', '#777'); - $("#server-tab").css( 'color', '#228bc0'); - - // Set an interval event to wait for all the pages to load, then show the content - var loading_config = setInterval(function() { - if (config_loaded == 1) { - $("#loading-page").hide(); - $("#everything").show(); - $("#server-content").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); - - }).fail(function(data) { - popup_err("Failed To Contact Server",data.message); - $("#everything").show(); - check_inst_alive(1); - loading_cfg = 0; - }); -} - - - -// 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() { - 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' ); - }); - } - - // 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(); - }); - $("#database-tab").on("click", function() { - $(".all-pages").hide(); - $("#database-content").show(); - }); - $("#monitor-tab").on("click", function() { - $(".all-pages").hide(); - $("#monitor-content").show(); - }); - $("#schema-tab").on("click", function() { - $(".all-pages").hide(); - $("#schema-content").show(); - }); - $("#replication-tab").on("click", 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/ds.jsx b/src/cockpit/389-console/src/ds.jsx new file mode 100644 index 0000000..439a850 --- /dev/null +++ b/src/cockpit/389-console/src/ds.jsx @@ -0,0 +1,1917 @@ +import cockpit from "cockpit"; +import React from "react"; +import PropTypes from "prop-types"; +import { Plugins } from "./plugins.jsx"; +import { Database } from "./database.jsx"; +import { Monitor } from "./monitor.jsx"; +import { Schema } from "./schema.jsx"; +import { Replication } from "./replication.jsx"; +import { Server } from "./server.jsx"; +import { ConfirmPopup, DoubleConfirmModal, NotificationController } from "./lib/notifications.jsx"; +import { BackupTable } from "./lib/database/databaseTables.jsx"; +import { BackupModal, RestoreModal, DeleteBackupModal } from "./lib/database/backups.jsx"; +import { log_cmd, bad_file_name } from "./lib/tools.jsx"; +import { + Nav, + NavItem, + DropdownButton, + MenuItem, + TabContainer, + TabContent, + TabPane, + ProgressBar, + FormControl, + FormGroup, + ControlLabel, + Radio, + Form, + noop, + Checkbox, + Spinner, + Row, + Modal, + Icon, + Col, + Button +} from "patternfly-react"; +import "./css/ds.css"; + +const staticStates = { + noPackage: ( +

+ There is no 389-ds-base package installed on this system. Sorry there is nothing + to manage... +

+ ), + noInsts:

There are no Directory Server instances to manage

, + notRunning: ( +

+ This server instance is not running, either start it from the Actions dropdown + menu, or choose a different instance +

+ ), + notConnecting: ( +

+ This server instance is running, but we can not connect to it. Check LDAPI is properly + configured on this instance. +

+ ) +}; + +export class DSInstance extends React.Component { + componentWillMount() { + this.checkPackageAndLoad(); + } + + constructor(props) { + super(props); + this.state = { + pageLoadingState: { state: "loading", jsx: "" }, + serverId: "", + instList: [], + backupRows: [], + notifications: [], + activeKey: 1, + wasActiveList: [1], + progressValue: 0, + loadingOperate: false, + + showDeleteConfirm: false, + modalSpinning: false, + modalChecked: false, + + showSchemaReloadModal: false, + showManageBackupsModal: false, + showCreateInstanceModal: false + }; + + this.handleServerIdChange = this.handleServerIdChange.bind(this); + this.handleFieldChange = this.handleFieldChange.bind(this); + this.addNotification = this.addNotification.bind(this); + this.removeNotification = this.removeNotification.bind(this); + this.handleNavSelect = this.handleNavSelect.bind(this); + this.loadInstanceList = this.loadInstanceList.bind(this); + this.loadBackups = this.loadBackups.bind(this); + this.setServerId = this.setServerId.bind(this); + this.checkPackageAndLoad = this.checkPackageAndLoad.bind(this); + this.updateProgress = this.updateProgress.bind(this); + this.openCreateInstanceModal = this.openCreateInstanceModal.bind(this); + this.closeCreateInstanceModal = this.closeCreateInstanceModal.bind(this); + this.operateInstance = this.operateInstance.bind(this); + this.openManageBackupsModal = this.openManageBackupsModal.bind(this); + this.closeManageBackupsModal = this.closeManageBackupsModal.bind(this); + this.openSchemaReloadModal = this.openSchemaReloadModal.bind(this); + this.closeSchemaReloadModal = this.closeSchemaReloadModal.bind(this); + this.removeInstance = this.removeInstance.bind(this); + this.showDeleteConfirm = this.showDeleteConfirm.bind(this); + this.closeDeleteConfirm = this.closeDeleteConfirm.bind(this); + } + + updateProgress(value) { + this.setState( + prevState => ({ + progressValue: prevState.progressValue + value + }), + () => { + if (this.state.progressValue >= 100) { + this.setState(prevState => ({ + pageLoadingState: { + ...prevState.pageLoadingState, + state: "success" + } + })); + } else { + this.setState(prevState => ({ + pageLoadingState: { + ...prevState.pageLoadingState, + state: "loading" + } + })); + } + } + ); + } + + setServerId(serverId, action) { + // First we need to check if the instance is alive and well + let cmd = ["dsctl", "-j", serverId, "status"]; + log_cmd("setServerId", "Test if instance is running ", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(status_data => { + let status_json = JSON.parse(status_data); + if (status_json.running) { + let cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + serverId + ".socket", + "backend", + "suffix", + "list", + "--suffix" + ]; + log_cmd("setServerId", "Test if instance is alive ", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(_ => { + this.setState( + { + serverId: serverId + }, + () => { + this.setState(prevState => ({ + pageLoadingState: { + ...prevState.pageLoadingState, + state: "success" + } + })); + this.loadBackups(); + } + ); + if (action === "restart") { + this.setState( + { + serverId: "" + }, + () => { + this.setState({ + serverId: serverId + }); + } + ); + } + this.updateProgress(25); + }) + .fail(err => { + let errMsg = JSON.parse(err); + console.log("setServerId failed: ", errMsg.desc); + this.setState( + { + pageLoadingState: { + state: "notConnecting", + jsx: staticStates["notConnecting"] + } + }, + () => { + this.setState({ + serverId: serverId + }); + } + ); + }); + this.updateProgress(25); + } else { + this.setState( + { + pageLoadingState: { + state: "notRunning", + jsx: staticStates["notRunning"] + } + }, + () => { + this.setState({ + serverId: serverId + }); + } + ); + } + }) + .fail(err => { + let errMsg = JSON.parse(err); + console.log("setServerId failed: ", errMsg.desc); + this.setState( + { + pageLoadingState: { + state: "notConnecting", + jsx: staticStates["notConnecting"] + } + }, + () => { + this.setState({ + serverId: serverId + }); + } + ); + }); + } + + checkPackageAndLoad() { + let cmd = ["rpm", "-q", "389-ds-base"]; + log_cmd("checkPackageAndLoad", "Check if 389-ds-base package is installed", cmd); + cockpit + .spawn(cmd, { superuser: true }) + .done(_ => { + this.updateProgress(25); + this.loadInstanceList(); + }) + .fail(_ => { + this.setState({ + pageLoadingState: { + state: "noPackage", + jsx: staticStates["noPackage"] + } + }); + }); + } + + loadInstanceList(serverId, action) { + if (serverId === undefined) { + this.setState(prevState => ({ + pageLoadingState: { + ...prevState.pageLoadingState, + state: "loading" + } + })); + } + let cmd = ["dsctl", "-l", "-j"]; + log_cmd("loadInstanceList", "Load the instance list select", cmd); + cockpit + .spawn(cmd, { superuser: true }) + .done(data => { + let myObject = JSON.parse(data); + this.setState({ + instList: myObject.insts, + loadingOperate: false + }); + // Set default value for the inst select + if (serverId !== undefined && serverId !== "") { + this.setState({ + wasActiveList: [this.state.activeKey] + }); + this.setServerId(serverId, action); + } else { + if (myObject.insts.length > 0) { + this.setState({ + wasActiveList: [this.state.activeKey] + }); + this.setServerId(myObject.insts[0].replace("slapd-", ""), action); + } else { + this.setState({ + serverId: "", + pageLoadingState: { + state: "noInsts", + jsx: staticStates["noInsts"] + } + }); + } + } + this.updateProgress(25); + }) + .fail(_ => { + this.setState({ + instList: [], + serverId: "", + loadingOperate: false, + pageLoadingState: { + state: "noInsts", + jsx: staticStates["noInsts"] + } + }); + }); + } + + loadBackups() { + this.setState({ + loadingOperate: true + }); + const cmd = ["dsctl", "-j", this.state.serverId, "backups"]; + log_cmd("loadBackupsDSInstance", "Load Backups", cmd); + cockpit.spawn(cmd, { superuser: true, err: "message" }).done(content => { + const config = JSON.parse(content); + let rows = []; + for (let row of config.items) { + rows.push({ name: row[0], date: [row[1]], size: [row[2]] }); + } + this.setState({ + backupRows: rows, + loadingOperate: false + }); + }); + } + + addNotification(type, message, timerdelay, persistent) { + this.setState(prevState => ({ + notifications: [ + ...prevState.notifications, + { + key: prevState.notifications.length + 1, + type: type, + persistent: persistent, + timerdelay: timerdelay, + message: message + } + ] + })); + } + + removeNotification(notificationToRemove) { + this.setState({ + notifications: this.state.notifications.filter( + notification => notificationToRemove.key !== notification.key + ) + }); + } + + handleNavSelect(key) { + this.setState({ + activeKey: key + }); + const { wasActiveList } = this.state; + if (!wasActiveList.includes(key)) { + let newList = wasActiveList.concat(key); + this.setState({ + wasActiveList: newList + }); + } + } + + handleServerIdChange(e) { + this.loadInstanceList(e.target.value); + } + + handleFieldChange(e) { + let value = e.target.type === "checkbox" ? e.target.checked : e.target.value; + if (e.target.type === "number") { + if (e.target.value) { + value = parseInt(e.target.value); + } else { + value = 1; + } + } + this.setState({ + [e.target.id]: value + }); + } + + removeInstance() { + this.operateInstance(); + this.closeDeleteConfirm(); + } + + operateInstance(e) { + this.setState({ + loadingOperate: true + }); + + let action = "remove"; + if (e !== undefined) { + action = e.target.id.split("-")[0]; + } + + let cmd = ["dsctl", "-j", this.state.serverId, action]; + if (action === "remove") { + cmd = [...cmd, "--do-it"]; + } + log_cmd("operateInstance", `Do ${action} the instance`, cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(_ => { + if (action === "remove") { + this.loadInstanceList(); + } else { + this.loadInstanceList(this.state.serverId, action); + } + if (action === "remove") { + this.addNotification("success", "Instance was successfully removed"); + } else { + this.addNotification("success", `Instance was successfully ${action}ed`); + } + }) + .fail(err => { + let errMsg = JSON.parse(err); + this.addNotification( + "error", + `Error during instance ${action} operation - ${errMsg.desc}` + ); + this.loadInstanceList(this.state.serverId, action); + }); + } + + openCreateInstanceModal() { + this.setState({ + showCreateInstanceModal: true + }); + } + + closeCreateInstanceModal() { + this.setState({ + showCreateInstanceModal: false + }); + } + + openManageBackupsModal() { + this.setState({ + showManageBackupsModal: true + }); + } + + closeManageBackupsModal() { + this.setState({ + showManageBackupsModal: false + }); + } + + openSchemaReloadModal() { + this.setState({ + showSchemaReloadModal: true + }); + } + + closeSchemaReloadModal() { + this.setState({ + showSchemaReloadModal: false + }); + } + + showDeleteConfirm() { + this.setState({ + showDeleteConfirm: true, + modalSpinning: false, + modalChecked: false + }); + } + + closeDeleteConfirm() { + this.setState({ + showDeleteConfirm: false, + modalSpinning: false, + modalChecked: false + }); + } + + render() { + const { + instList, + serverId, + progressValue, + notifications, + pageLoadingState, + loadingOperate + } = this.state; + + let mainContent = ""; + + if (pageLoadingState.state === "loading") { + mainContent = ( +
+
+

Loading Directory Server Configuration...

+

+ +

+
+ +
+
+
+ ); + } else if (pageLoadingState.state === "noInsts") { + mainContent = ( +
+ {pageLoadingState.jsx} +

+ +

+
+ ); + } else { + mainContent = ( +
+ {pageLoadingState.jsx} +
+ ); + } + + let operateSpinner = ""; + if (loadingOperate) { + operateSpinner = ; + } + + return ( +
+ + {pageLoadingState.state !== "loading" && + pageLoadingState.state !== "noInsts" && + pageLoadingState.state !== "noPackage" ? ( + + ) : ( +
+ )} + {serverId !== "" && + (pageLoadingState.state === "success" || pageLoadingState.state === "loading") ? ( +
+ + +
+ ) : ( +
{mainContent}
+ )} + + + + +
+ ); + } +} + +class CreateInstanceModal extends React.Component { + constructor(props) { + super(props); + this.state = { + createServerId: "", + createPort: 389, + createSecurePort: 636, + createDM: "cn=Directory Manager", + createDMPassword: "", + createDMPasswordConfirm: "", + createDBSuffix: "", + createDBName: "", + createTLSCert: true, + createInitDB: "", + loadingCreate: false + }; + + this.handleFieldChange = this.handleFieldChange.bind(this); + this.createInstance = this.createInstance.bind(this); + } + + handleFieldChange(e) { + let value = e.target.type === "checkbox" ? e.target.checked : e.target.value; + if (e.target.type === "number") { + if (e.target.value) { + value = parseInt(e.target.value); + } else { + value = 1; + } + } + this.setState({ + [e.target.id]: value + }); + } + + createInstance() { + const { + createServerId, + createPort, + createSecurePort, + createDM, + createDMPassword, + createDMPasswordConfirm, + createDBSuffix, + createDBName, + createTLSCert, + createInitDB + } = this.state; + const { closeHandler, addNotification, loadInstanceList } = this.props; + + let setup_inf = + "[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"; + + // Server ID + let newServerId = createServerId; + if (newServerId === "") { + addNotification("warning", "Instance Name is required."); + return; + } + newServerId = newServerId.replace(/^slapd-/i, ""); // strip "slapd-" + if (newServerId.length > 128) { + addNotification( + "warning", + "Instance name is too long, it must not exceed 128 characters" + ); + return; + } + if (newServerId.match(/^[#%:A-Za-z0-9_-]+$/g)) { + setup_inf = setup_inf.replace("INST_NAME", newServerId); + } else { + addNotification( + "warning", + "Instance name can only contain letters, numbers, and: # % : - _" + ); + return; + } + + // Port + if (createPort < 1 || createPort > 65535) { + addNotification("warning", "Port must be a number between 1 and 65534!"); + return; + } else { + setup_inf = setup_inf.replace("PORT", createPort); + } + + // Secure Port + if (createSecurePort < 1 || createSecurePort > 65535) { + addNotification("warning", "Secure Port must be a number between 1 and 65534!"); + return; + } else { + setup_inf = setup_inf.replace("SECURE_PORT", createSecurePort); + } + + // Root DN + if (createDM === "") { + addNotification("warning", "You must provide a Directory Manager DN"); + return; + } else { + setup_inf = setup_inf.replace("ROOTDN", createDM); + } + + // Setup Self-Signed Certs + if (createTLSCert) { + setup_inf = setup_inf.replace("SELF_SIGN", "True"); + } else { + setup_inf = setup_inf.replace("SELF_SIGN", "False"); + } + + // Root DN password + if (createDMPassword != createDMPasswordConfirm) { + addNotification("warning", "Directory Manager passwords do not match!"); + return; + } else if (createDMPassword == "") { + addNotification("warning", "Directory Manager password can not be empty!"); + return; + } else if (createDMPassword.length < 8) { + addNotification( + "warning", + "Directory Manager password must have at least 8 characters" + ); + return; + } else { + setup_inf = setup_inf.replace("ROOTPW", createDMPassword); + } + + // Backend/Suffix + if ( + (createDBName != "" && createDBSuffix == "") || + (createDBName == "" && createDBSuffix != "") + ) { + if (createDBName == "") { + addNotification( + "warning", + "If you specify a backend suffix, you must also specify a backend name" + ); + return; + } else { + addNotification( + "warning", + "If you specify a backend name, you must also specify a backend suffix" + ); + return; + } + } + if (createDBName != "") { + // We definitely have a backend name and suffix, next validate the suffix is a DN + let dn_regex = new RegExp("^([A-Za-z]+=.*)"); + if (dn_regex.test(createDBSuffix)) { + // It's valid, add it + setup_inf += "\n[backend-" + createDBName + "]\nsuffix = " + createDBSuffix + "\n"; + } else { + // Not a valid DN + addNotification("warning", "Invalid DN for Backend Suffix"); + return; + } + + if (createInitDB === "createSample") { + setup_inf += "\nsample_entries = yes\n"; + } + if (createInitDB === "createSuffix") { + 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 + */ + this.setState({ + loadingCreate: true + }); + cockpit + + .spawn(["hostnamectl", "status", "--static"], { superuser: true, err: "message" }) + .fail(err => { + let errMsg = JSON.parse(err); + this.setState({ + loadingCreate: false + }); + addNotification("error", `Failed to get hostname!", ${errMsg.desc}`); + }) + .done(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(err => { + let errMsg = JSON.parse(err); + this.setState({ + loadingCreate: false + }); + addNotification( + "error", + `Failed to create installation file!" ${errMsg.desc}` + ); + }) + .done(_ => { + /* + * 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(err => { + let errMsg = JSON.parse(err); + cockpit.spawn(rm_cmd, { superuser: true }); // Remove Inf file with clear text password + this.setState({ + loadingCreate: false + }); + addNotification( + "error", + `Failed to set permission on setup file ${setup_file}: ${ + errMsg.desc + }` + ); + }) + .done(_ => { + /* + * 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(err => { + let errMsg = JSON.parse(err); + this.setState({ + loadingCreate: false + }); + addNotification( + "error", + `Failed to populate installation file! ${errMsg.desc}` + ); + }) + .done(_ => { + /* + * Next, create the instance... + */ + let cmd = ["dscreate", "from-file", setup_file]; + cockpit + .spawn(cmd, { + superuser: true, + err: "message" + }) + .fail(_ => { + cockpit.spawn(rm_cmd, { superuser: true }); // Remove Inf file with clear text password + this.setState({ + loadingCreate: false + }); + addNotification( + "error", + "Failed to create instance!" + ); + }) + .done(_ => { + // Success!!! Now cleanup everything up... + cockpit.spawn(rm_cmd, { superuser: true }); // Remove Inf file with clear text password + this.setState({ + loadingCreate: false + }); + + loadInstanceList(createServerId); + addNotification( + "success", + `Successfully created instance: slapd-${createServerId}` + ); + closeHandler(); + }); + }); + }); + }); + }); + } + + render() { + const { showModal, closeHandler } = this.props; + + const { + loadingCreate, + createServerId, + createPort, + createSecurePort, + createDM, + createDMPassword, + createDMPasswordConfirm, + createDBSuffix, + createDBName, + createTLSCert, + createInitDB + } = this.state; + + let createSpinner = ""; + if (loadingCreate) { + createSpinner = ( + +
+ + Creating instance... +
+
+ ); + } + + return ( + +
+ + + Create New Server Instance + + +
+ + + Instance Name + + + + + + + + Port + + + + + + + + Secure Port + + + + + + + + Create Self-Signed TLS Certificate DB + + + + + + + + Directory Manager DN + + + + + + + + Directory Manager Password + + + + + + + + Confirm Password + + + + + +
+
Optional Database Settings
+ + + Database Suffix + + + + + + + + Database Name + + + + + + + + + Do Not Initialize Database + + + + + + + Create Suffix Entry + + + + + + + Create Suffix Entry And Add Sample Entries + + + +
+ {createSpinner} +
+ + + + +
+
+ ); + } +} + +CreateInstanceModal.propTypes = { + showModal: PropTypes.bool, + closeHandler: PropTypes.func, + addNotification: PropTypes.func, + loadInstanceList: PropTypes.func +}; + +CreateInstanceModal.defaultProps = { + showModal: false, + closeHandler: noop, + addNotification: noop, + loadInstanceList: noop +}; + +export class SchemaReloadModal extends React.Component { + constructor(props) { + super(props); + this.state = { + reloadSchemaDir: "", + loadingSchemaTask: false + }; + + this.reloadSchema = this.reloadSchema.bind(this); + this.handleFieldChange = this.handleFieldChange.bind(this); + } + + handleFieldChange(e) { + this.setState({ + [e.target.id]: e.target.value + }); + } + + reloadSchema(e) { + const { addNotification, serverId, closeHandler } = this.props; + const { reloadSchemaDir } = this.state; + + this.setState({ + loadingSchemaTask: true + }); + + let cmd = ["dsconf", "-j", serverId, "schema", "reload", "--wait"]; + if (reloadSchemaDir !== "") { + cmd = [...cmd, "--schemadir", reloadSchemaDir]; + } + log_cmd("reloadSchemaDir", "Reload schema files", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(data => { + addNotification("success", "Successfully reloaded schema"); + this.setState({ + loadingSchemaTask: false + }); + closeHandler(); + }) + .fail(err => { + let errMsg = JSON.parse(err); + addNotification("error", `Failed to reload schema files - ${errMsg.desc}`); + closeHandler(); + }); + } + + render() { + const { loadingSchemaTask, reloadSchemaDir } = this.state; + const { showModal, closeHandler } = this.props; + + let spinner = ""; + if (loadingSchemaTask) { + spinner = ( + +
+ + Reloading schema files... +
+
+ ); + } + + return ( + +
+ + + Reload Schema Files + + +
+ + + Schema File Directory: + + + + + + {spinner} +
+
+ + + + +
+
+ ); + } +} + +SchemaReloadModal.propTypes = { + showModal: PropTypes.bool, + closeHandler: PropTypes.func, + addNotification: PropTypes.func, + serverId: PropTypes.string +}; + +SchemaReloadModal.defaultProps = { + showModal: false, + closeHandler: noop, + addNotification: noop, + serverId: "" +}; + +class ManageBackupsModal extends React.Component { + constructor(props) { + super(props); + this.state = { + activeKey: 1, + showConfirmBackupDelete: false, + showConfirmBackup: false, + showConfirmRestore: false, + showConfirmRestoreReplace: false, + showConfirmLDIFReplace: false, + showRestoreSpinningModal: false, + showDelBackupSpinningModal: false, + showBackupModal: false, + backupSpinning: false, + backupName: "", + deleteBackup: "", + errObj: {} + }; + + this.handleNavSelect = this.handleNavSelect.bind(this); + this.handleChange = this.handleChange.bind(this); + + // Backups + this.doBackup = this.doBackup.bind(this); + this.deleteBackup = this.deleteBackup.bind(this); + this.restoreBackup = this.restoreBackup.bind(this); + this.showConfirmRestore = this.showConfirmRestore.bind(this); + this.closeConfirmRestore = this.closeConfirmRestore.bind(this); + this.showConfirmBackup = this.showConfirmBackup.bind(this); + this.closeConfirmBackup = this.closeConfirmBackup.bind(this); + this.showConfirmBackupDelete = this.showConfirmBackupDelete.bind(this); + this.closeConfirmBackupDelete = this.closeConfirmBackupDelete.bind(this); + this.showBackupModal = this.showBackupModal.bind(this); + this.closeBackupModal = this.closeBackupModal.bind(this); + this.showRestoreSpinningModal = this.showRestoreSpinningModal.bind(this); + this.closeRestoreSpinningModal = this.closeRestoreSpinningModal.bind(this); + this.showDelBackupSpinningModal = this.showDelBackupSpinningModal.bind(this); + this.closeDelBackupSpinningModal = this.closeDelBackupSpinningModal.bind(this); + this.validateBackup = this.validateBackup.bind(this); + this.closeConfirmRestoreReplace = this.closeConfirmRestoreReplace.bind(this); + } + + closeExportModal() { + this.setState({ + showExportModal: false + }); + } + + showDelBackupSpinningModal() { + this.setState({ + showDelBackupSpinningModal: true + }); + } + + closeDelBackupSpinningModal() { + this.setState({ + showDelBackupSpinningModal: false + }); + } + + showRestoreSpinningModal() { + this.setState({ + showRestoreSpinningModal: true + }); + } + + closeRestoreSpinningModal() { + this.setState({ + showRestoreSpinningModal: false + }); + } + + showBackupModal() { + this.setState({ + showBackupModal: true, + backupSpinning: false, + backupName: "" + }); + } + + closeBackupModal() { + this.setState({ + showBackupModal: false + }); + } + + showConfirmBackup(item) { + // call deleteLDIF + this.setState({ + showConfirmBackup: true, + backupName: item.name + }); + } + + closeConfirmBackup() { + // call importLDIF + this.setState({ + showConfirmBackup: false + }); + } + + showConfirmRestore(item) { + this.setState({ + showConfirmRestore: true, + backupName: item.name + }); + } + + closeConfirmRestore() { + // call importLDIF + this.setState({ + showConfirmRestore: false + }); + } + + showConfirmBackupDelete(item) { + // calls deleteBackup + this.setState({ + showConfirmBackupDelete: true, + backupName: item.name + }); + } + + closeConfirmBackupDelete() { + // call importLDIF + this.setState({ + showConfirmBackupDelete: false + }); + } + + closeConfirmRestoreReplace() { + this.setState({ + showConfirmRestoreReplace: false + }); + } + + validateBackup() { + for (let i = 0; i < this.props.backups.length; i++) { + if (this.state.backupName == this.props.backups[i]["name"]) { + this.setState({ + showConfirmRestoreReplace: true + }); + return; + } + } + this.doBackup(); + } + + doBackup() { + this.setState({ + backupSpinning: true + }); + + let cmd = ["dsctl", "-j", this.props.serverId, "status"]; + cockpit + .spawn(cmd, { superuser: true }) + .done(status_data => { + let status_json = JSON.parse(status_data); + if (status_json.running == true) { + let cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "backup", + "create" + ]; + if (this.state.backupName != "") { + if (bad_file_name(this.state.backupName)) { + this.props.addNotification( + "warning", + `Backup name should not be a path. All backups are stored in the server's backup directory` + ); + return; + } + cmd.push(this.state.backupName); + } + + log_cmd("doBackup", "Add backup task online", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + this.props.reload(); + this.closeBackupModal(); + this.props.addNotification("success", `Server has been backed up`); + }) + .fail(err => { + let errMsg = JSON.parse(err); + this.props.reload(); + this.closeBackupModal(); + this.props.addNotification( + "error", + `Failure backing up server - ${errMsg.desc}` + ); + }); + } else { + const cmd = ["dsctl", "-j", this.props.serverId, "db2bak"]; + if (this.state.backupName != "") { + if (bad_file_name(this.state.backupName)) { + this.props.addNotification( + "warning", + `Backup name should not be a path. All backups are stored in the server's backup directory` + ); + return; + } + cmd.push(this.state.backupName); + } + log_cmd("doBackup", "Doing backup of the server offline", cmd); + cockpit + .spawn(cmd, { superuser: true }) + .done(content => { + this.props.reload(); + this.closeBackupModal(); + this.props.addNotification("success", `Server has been backed up`); + }) + .fail(err => { + let errMsg = JSON.parse(err); + this.props.reload(); + this.closeBackupModal(); + this.props.addNotification( + "error", + `Failure backing up server - ${errMsg.desc}` + ); + }); + } + }) + .fail(err => { + let errMsg = JSON.parse(err); + console.log("Failed to check the server status", errMsg.desc); + }); + } + + restoreBackup() { + this.showRestoreSpinningModal(); + let cmd = ["dsctl", "-j", this.props.serverId, "status"]; + cockpit + .spawn(cmd, { superuser: true }) + .done(status_data => { + let status_json = JSON.parse(status_data); + if (status_json.running == true) { + const cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "backup", + "restore", + this.state.backupName + ]; + log_cmd("restoreBackup", "Restoring server online", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + this.closeRestoreSpinningModal(); + this.props.addNotification("success", `Server has been restored`); + }) + .fail(err => { + let errMsg = JSON.parse(err); + this.closeRestoreSpinningModal(); + this.props.addNotification( + "error", + `Failure restoring up server - ${errMsg.desc}` + ); + }); + } else { + const cmd = [ + "dsctl", + "-j", + this.props.serverId, + "bak2db", + this.state.backupName + ]; + log_cmd("restoreBackup", "Restoring server offline", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + this.closeRestoreSpinningModal(); + this.props.addNotification("success", `Server has been restored`); + }) + .fail(err => { + let errMsg = JSON.parse(err); + this.closeRestoreSpinningModal(); + this.props.addNotification( + "error", + `Failure restoring up server - ${errMsg.desc}` + ); + }); + } + }) + .fail(err => { + let errMsg = JSON.parse(err); + console.log("Failed to check the server status", errMsg.desc); + }); + } + + deleteBackup(e) { + // Show confirmation + this.showDelBackupSpinningModal(); + + const cmd = [ + "dsctl", + "-j", + this.props.serverId, + "backups", + "--delete", + this.state.backupName + ]; + log_cmd("deleteBackup", "Deleting backup", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + this.props.reload(); + this.closeDelBackupSpinningModal(); + this.props.addNotification("success", `Backup was successfully deleted`); + }) + .fail(err => { + let errMsg = JSON.parse(err); + this.props.reload(); + this.closeDelBackupSpinningModal(); + this.props.addNotification("error", `Failure deleting backup - ${errMsg.desc}`); + }); + } + + handleNavSelect(key) { + this.setState({ activeKey: key }); + } + + handleChange(e) { + const 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 + }); + } + + render() { + const { showModal, closeHandler, backups, reload, loadingBackup } = this.props; + + let backupSpinner = ""; + if (loadingBackup) { + backupSpinner = ( + +
+ + Creating instance... +
+
+ ); + } + + return ( +
+ +
+ + + Manage Backups + + +
+ +
+ {backupSpinner} +
+ + + + +
+
+ + + + + + +
+ ); + } +} + +ManageBackupsModal.propTypes = { + showModal: PropTypes.bool, + closeHandler: PropTypes.func, + addNotification: PropTypes.func, + serverId: PropTypes.string +}; + +ManageBackupsModal.defaultProps = { + showModal: false, + closeHandler: noop, + addNotification: noop, + serverId: "" +}; + +export default DSInstance; diff --git a/src/cockpit/389-console/src/index.es6 b/src/cockpit/389-console/src/index.es6 index 9344ffa..91abfcf 100644 --- a/src/cockpit/389-console/src/index.es6 +++ b/src/cockpit/389-console/src/index.es6 @@ -1,86 +1,7 @@ import React from "react"; import ReactDOM from "react-dom"; -import { Plugins } from "./plugins.jsx"; -import { Database } from "./database.jsx"; -import { Monitor } from "./monitor.jsx"; -import { Schema } from "./schema.jsx"; -import { Replication } from "./replication.jsx"; -import { Server } from "./server.jsx"; +import { DSInstance } from "./ds.jsx"; -var serverIdElem; - -function renderReactDOM(clear) { - // We should clear the React properties on the instance removal - if (clear) { - serverIdElem = ""; - } else { - serverIdElem = document - .getElementById("select-server") - .value.replace("slapd-", ""); - } - let d = new Date(); - let tabKey = d.getTime(); - - // Server Tab - ReactDOM.render( - , - document.getElementById("server") - ); - // Plugins Tab - ReactDOM.render( - , - document.getElementById("plugins") - ); - - // Database tab - ReactDOM.render( - , - document.getElementById("database") - ); - - // Replication tab - ReactDOM.render( - , - document.getElementById("replication") - ); - - // Monitor tab - ReactDOM.render( - , - document.getElementById("monitor") - ); - - // Schema tab - ReactDOM.render( - , - document.getElementById("schema") - ); -} - -// We have to create the wrappers because this is no simple way -// to pass arguments to the listener's callback function -function renderReactWrapper() { - renderReactDOM(false); -} -function renderClearWrapper() { - renderReactDOM(true); -} - -document.addEventListener("DOMContentLoaded", function() { - var init_config = setInterval(function() { - serverIdElem = document.getElementById("select-server"); - if (serverIdElem != null && serverIdElem.value.startsWith("slapd-")) { - document - .getElementById("select-server") - .addEventListener("change", renderReactWrapper); - document - .getElementById("reload-page") - .addEventListener("click", renderReactWrapper); - document - .getElementById("remove-server-btn") - .addEventListener("click", renderClearWrapper); - renderReactDOM(); - clearInterval(init_config); - } - }, 250); +document.addEventListener("DOMContentLoaded", function () { + ReactDOM.render(React.createElement(DSInstance, {}), document.getElementById('dsinstance')); }); diff --git a/src/cockpit/389-console/src/index.html b/src/cockpit/389-console/src/index.html index 1df2d19..4994f23 100644 --- a/src/cockpit/389-console/src/index.html +++ b/src/cockpit/389-console/src/index.html @@ -6,481 +6,16 @@ - - - - - - - - - - - - - - - - - - - - - + +
+ + diff --git a/src/cockpit/389-console/src/lib/database/backups.jsx b/src/cockpit/389-console/src/lib/database/backups.jsx index 6204673..60a50f5 100644 --- a/src/cockpit/389-console/src/lib/database/backups.jsx +++ b/src/cockpit/389-console/src/lib/database/backups.jsx @@ -865,7 +865,7 @@ export class BackupModal extends React.Component { } } -class RestoreModal extends React.Component { +export class RestoreModal extends React.Component { render() { const { showModal, @@ -912,7 +912,7 @@ class RestoreModal extends React.Component { } } -class DeleteBackupModal extends React.Component { +export class DeleteBackupModal extends React.Component { render() { const { showModal, diff --git a/src/cockpit/389-console/src/lib/database/chaining.jsx b/src/cockpit/389-console/src/lib/database/chaining.jsx index 63cf899..b67c562 100644 --- a/src/cockpit/389-console/src/lib/database/chaining.jsx +++ b/src/cockpit/389-console/src/lib/database/chaining.jsx @@ -1265,7 +1265,7 @@ export class ChainingConfig extends React.Component { checked={this.state.modalChecked} mTitle="Delete Database Link" mMsg="Are you really sure you want to delete this database link?" - mSpinningMsg="Deleting Database Linkt ..." + mSpinningMsg="Deleting Database Link..." mBtnName="Delete Database Link" />
diff --git a/src/cockpit/389-console/src/lib/plugins/attributeUniqueness.jsx b/src/cockpit/389-console/src/lib/plugins/attributeUniqueness.jsx index 0c9af6a..66453f9 100644 --- a/src/cockpit/389-console/src/lib/plugins/attributeUniqueness.jsx +++ b/src/cockpit/389-console/src/lib/plugins/attributeUniqueness.jsx @@ -23,12 +23,17 @@ import "../../css/ds.css"; class AttributeUniqueness extends React.Component { componentWillMount() { - this.loadConfigs(); + if (this.props.wasActiveList.includes(5)) { + if (this.state.firstLoad) { + this.loadConfigs(); + } + } } constructor(props) { super(props); this.state = { + firstLoad: true, configRows: [], attributes: [], objectClasses: [], @@ -81,6 +86,9 @@ class AttributeUniqueness extends React.Component { } loadConfigs() { + this.setState({ + firstLoad: false + }); // Get all the attributes and matching rules now const cmd = [ "dsconf", @@ -169,25 +177,18 @@ class AttributeUniqueness extends React.Component { topEntryOc: configEntry["uniqueness-top-entry-oc"] === undefined ? [] - : [ - configEntry["uniqueness-top-entry-oc"][0] - ], + : [configEntry["uniqueness-top-entry-oc"][0]], subtreeEnriesOc: configEntry["uniqueness-subtree-entries-oc"] === undefined ? [] - : [ - configEntry["uniqueness-subtree-entries-oc"][0] - ] + : [configEntry["uniqueness-subtree-entries-oc"][0]] }); if (configEntry["uniqueness-attribute-name"] === undefined) { this.setState({ attrNames: [] }); } else { for (let value of configEntry["uniqueness-attribute-name"]) { - configAttrNamesList = [ - ...configAttrNamesList, - value - ]; + configAttrNamesList = [...configAttrNamesList, value]; } this.setState({ attrNames: configAttrNamesList }); } @@ -195,10 +196,7 @@ class AttributeUniqueness extends React.Component { this.setState({ subtrees: [] }); } else { for (let value of configEntry["uniqueness-subtrees"]) { - configSubtreesList = [ - ...configSubtreesList, - value - ]; + configSubtreesList = [...configSubtreesList, value]; } this.setState({ subtrees: configSubtreesList }); } diff --git a/src/cockpit/389-console/src/lib/plugins/autoMembership.jsx b/src/cockpit/389-console/src/lib/plugins/autoMembership.jsx index 015b74d..6de8d3a 100644 --- a/src/cockpit/389-console/src/lib/plugins/autoMembership.jsx +++ b/src/cockpit/389-console/src/lib/plugins/autoMembership.jsx @@ -21,12 +21,17 @@ import "../../css/ds.css"; class AutoMembership extends React.Component { componentWillMount() { - this.loadDefinitions(); + if (this.props.wasActiveList.includes(5)) { + if (this.state.firstLoad) { + this.loadDefinitions(); + } + } } constructor(props) { super(props); this.state = { + firstLoad: true, definitionRows: [], regexRows: [], regexesToDelete: [], @@ -82,6 +87,9 @@ class AutoMembership extends React.Component { } loadDefinitions() { + this.setState({ + firstLoad: false + }); const cmd = [ "dsconf", "-j", @@ -213,9 +221,7 @@ class AutoMembership extends React.Component { } else { let groupingAttr = definitionEntry["automembergroupingattr"][0]; this.setState({ - groupingAttrMember: [ - groupingAttr.split(":")[0] - ], + groupingAttrMember: [groupingAttr.split(":")[0]], groupingAttrEntry: groupingAttr.split(":")[1] }); } diff --git a/src/cockpit/389-console/src/lib/plugins/dna.jsx b/src/cockpit/389-console/src/lib/plugins/dna.jsx index df5c9f8..4539255 100644 --- a/src/cockpit/389-console/src/lib/plugins/dna.jsx +++ b/src/cockpit/389-console/src/lib/plugins/dna.jsx @@ -22,12 +22,17 @@ import "../../css/ds.css"; class DNA extends React.Component { componentWillMount() { - this.loadConfigs(); + if (this.props.wasActiveList.includes(5)) { + if (this.state.firstLoad) { + this.loadConfigs(); + } + } } constructor(props) { super(props); this.state = { + firstLoad: true, configRows: [], sharedConfigRows: [], attributes: [], @@ -121,6 +126,9 @@ class DNA extends React.Component { } loadConfigs() { + this.setState({ + firstLoad: false + }); const cmd = [ "dsconf", "-j", diff --git a/src/cockpit/389-console/src/lib/plugins/linkedAttributes.jsx b/src/cockpit/389-console/src/lib/plugins/linkedAttributes.jsx index b9bd253..30900f7 100644 --- a/src/cockpit/389-console/src/lib/plugins/linkedAttributes.jsx +++ b/src/cockpit/389-console/src/lib/plugins/linkedAttributes.jsx @@ -21,12 +21,17 @@ import "../../css/ds.css"; class LinkedAttributes extends React.Component { componentWillMount() { - this.loadConfigs(); + if (this.props.wasActiveList.includes(5)) { + if (this.state.firstLoad) { + this.loadConfigs(); + } + } } constructor(props) { super(props); this.state = { + firstLoad: true, configRows: [], attributes: [], @@ -60,6 +65,9 @@ class LinkedAttributes extends React.Component { } loadConfigs() { + this.setState({ + firstLoad: false + }); // Get all the attributes and matching rules now const cmd = [ "dsconf", @@ -136,15 +144,11 @@ class LinkedAttributes extends React.Component { linkType: configEntry["linktype"] === undefined ? [] - : [ - configEntry["linktype"][0] - ], + : [configEntry["linktype"][0]], managedType: configEntry["managedtype"] === undefined ? [] - : [ - configEntry["managedtype"][0] - ], + : [configEntry["managedtype"][0]], linkScope: configEntry["linkscope"] === undefined ? "" diff --git a/src/cockpit/389-console/src/lib/plugins/memberOf.jsx b/src/cockpit/389-console/src/lib/plugins/memberOf.jsx index 391dd3d..9cbceff 100644 --- a/src/cockpit/389-console/src/lib/plugins/memberOf.jsx +++ b/src/cockpit/389-console/src/lib/plugins/memberOf.jsx @@ -21,9 +21,13 @@ import "../../css/ds.css"; class MemberOf extends React.Component { componentWillMount(prevProps) { - this.getObjectClasses(); - this.getAttributes(); - this.updateFields(); + if (this.props.wasActiveList.includes(5)) { + if (this.state.firstLoad) { + this.getObjectClasses(); + this.getAttributes(); + this.updateFields(); + } + } } componentDidUpdate(prevProps) { @@ -50,6 +54,7 @@ class MemberOf extends React.Component { this.toggleFixupModal = this.toggleFixupModal.bind(this); this.state = { + firstLoad: true, objectClasses: [], attributeTypes: [], @@ -182,9 +187,7 @@ class MemberOf extends React.Component { configAutoAddOC: configEntry["memberofautoaddoc"] === undefined ? [] - : [ - configEntry["memberofautoaddoc"][0], - ], + : [configEntry["memberofautoaddoc"][0]], configAllBackends: !( configEntry["memberofallbackends"] === undefined || configEntry["memberofallbackends"][0] == "off" @@ -210,10 +213,7 @@ class MemberOf extends React.Component { this.setState({ configAttr: [] }); } else { for (let value of configEntry["memberofattr"]) { - configAttrObjectList = [ - ...configAttrObjectList, - value - ]; + configAttrObjectList = [...configAttrObjectList, value]; } this.setState({ configAttr: configAttrObjectList }); } @@ -221,10 +221,7 @@ class MemberOf extends React.Component { this.setState({ configGroupAttr: [] }); } else { for (let value of configEntry["memberofgroupattr"]) { - configGroupAttrObjectList = [ - ...configGroupAttrObjectList, - value - ]; + configGroupAttrObjectList = [...configGroupAttrObjectList, value]; } this.setState({ configGroupAttr: configGroupAttrObjectList @@ -418,9 +415,7 @@ class MemberOf extends React.Component { memberOfAutoAddOC: pluginRow["memberofautoaddoc"] === undefined ? [] - : [ - pluginRow["memberofautoaddoc"][0], - ], + : [pluginRow["memberofautoaddoc"][0]], memberOfAllBackends: !( pluginRow["memberofallbackends"] === undefined || pluginRow["memberofallbackends"][0] == "off" @@ -446,10 +441,7 @@ class MemberOf extends React.Component { this.setState({ memberOfAttr: [] }); } else { for (let value of pluginRow["memberofattr"]) { - memberOfAttrObjectList = [ - ...memberOfAttrObjectList, - value - ]; + memberOfAttrObjectList = [...memberOfAttrObjectList, value]; } this.setState({ memberOfAttr: memberOfAttrObjectList }); } @@ -457,10 +449,7 @@ class MemberOf extends React.Component { this.setState({ memberOfGroupAttr: [] }); } else { for (let value of pluginRow["memberofgroupattr"]) { - memberOfGroupAttrObjectList = [ - ...memberOfGroupAttrObjectList, - value - ]; + memberOfGroupAttrObjectList = [...memberOfGroupAttrObjectList, value]; } this.setState({ memberOfGroupAttr: memberOfGroupAttrObjectList @@ -470,6 +459,9 @@ class MemberOf extends React.Component { } getObjectClasses() { + this.setState({ + firstLoad: false + }); const oc_cmd = [ "dsconf", "-j", @@ -1037,10 +1029,7 @@ class MemberOf extends React.Component { /> - @@ -1078,10 +1067,7 @@ class MemberOf extends React.Component { - diff --git a/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx b/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx index b3ede3f..d0c4787 100644 --- a/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx +++ b/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx @@ -22,14 +22,19 @@ import { log_cmd } from "../tools.jsx"; import "../../css/ds.css"; class PassthroughAuthentication extends React.Component { - componentWillMount() { - this.loadPAMConfigs(); - this.loadURLs(); + componentDidUpdate() { + if (this.props.wasActiveList.includes(5)) { + if (this.state.firstLoad) { + this.loadPAMConfigs(); + this.loadURLs(); + } + } } constructor(props) { super(props); this.state = { + firstLoad: true, pamConfigRows: [], urlRows: [], attributes: [], @@ -101,6 +106,9 @@ class PassthroughAuthentication extends React.Component { } loadPAMConfigs() { + this.setState({ + firstLoad: false + }); const cmd = [ "dsconf", "-j", @@ -247,10 +255,7 @@ class PassthroughAuthentication extends React.Component { this.setState({ pamExcludeSuffix: [] }); } else { for (let value of pamConfigEntry["pamexcludesuffix"]) { - pamExcludeSuffixList = [ - ...pamExcludeSuffixList, - value - ]; + pamExcludeSuffixList = [...pamExcludeSuffixList, value]; } this.setState({ pamExcludeSuffix: pamExcludeSuffixList @@ -261,10 +266,7 @@ class PassthroughAuthentication extends React.Component { this.setState({ pamIncludeSuffix: [] }); } else { for (let value of pamConfigEntry["pamincludesuffix"]) { - pamIncludeSuffixList = [ - ...pamIncludeSuffixList, - value - ]; + pamIncludeSuffixList = [...pamIncludeSuffixList, value]; } this.setState({ pamIncludeSuffix: pamIncludeSuffixList diff --git a/src/cockpit/389-console/src/lib/plugins/referentialIntegrity.jsx b/src/cockpit/389-console/src/lib/plugins/referentialIntegrity.jsx index 700c188..7419b84 100644 --- a/src/cockpit/389-console/src/lib/plugins/referentialIntegrity.jsx +++ b/src/cockpit/389-console/src/lib/plugins/referentialIntegrity.jsx @@ -20,8 +20,12 @@ import "../../css/ds.css"; class ReferentialIntegrity extends React.Component { componentWillMount(prevProps) { - this.getAttributes(); - this.updateFields(); + if (this.props.wasActiveList.includes(5)) { + if (this.state.firstLoad) { + this.getAttributes(); + this.updateFields(); + } + } } componentDidUpdate(prevProps) { @@ -34,6 +38,7 @@ class ReferentialIntegrity extends React.Component { super(props); this.state = { + firstLoad: true, updateDelay: "", membershipAttr: [], entryScope: "", @@ -138,10 +143,7 @@ class ReferentialIntegrity extends React.Component { this.setState({ configMembershipAttr: [] }); } else { for (let value of pluginRow["referint-membership-attr"]) { - membershipAttrList = [ - ...membershipAttrList, - value - ]; + membershipAttrList = [...membershipAttrList, value]; } this.setState({ configMembershipAttr: membershipAttrList @@ -342,6 +344,9 @@ class ReferentialIntegrity extends React.Component { } getAttributes() { + this.setState({ + firstLoad: false + }); const attr_cmd = [ "dsconf", "-j", @@ -746,10 +751,7 @@ class ReferentialIntegrity extends React.Component { /> - diff --git a/src/cockpit/389-console/src/lib/plugins/retroChangelog.jsx b/src/cockpit/389-console/src/lib/plugins/retroChangelog.jsx index c1ed953..e17a903 100644 --- a/src/cockpit/389-console/src/lib/plugins/retroChangelog.jsx +++ b/src/cockpit/389-console/src/lib/plugins/retroChangelog.jsx @@ -18,8 +18,12 @@ import "../../css/ds.css"; class RetroChangelog extends React.Component { componentWillMount(prevProps) { - this.getAttributes(); - this.updateFields(); + if (this.props.wasActiveList.includes(5)) { + if (this.state.firstLoad) { + this.getAttributes(); + this.updateFields(); + } + } } componentDidUpdate(prevProps) { @@ -32,6 +36,7 @@ class RetroChangelog extends React.Component { super(props); this.state = { + firstLoad: true, isReplicated: false, attribute: [], directory: "", @@ -69,9 +74,7 @@ class RetroChangelog extends React.Component { attribute: pluginRow["nsslapd-attribute"] === undefined ? [] - : [ - pluginRow["nsslapd-attribute"][0] - ], + : [pluginRow["nsslapd-attribute"][0]], directory: pluginRow["nsslapd-changelogdir"] === undefined ? "" @@ -89,6 +92,7 @@ class RetroChangelog extends React.Component { } getAttributes() { + this.setState({ firstLoad: false }); const attr_cmd = [ "dsconf", "-j", diff --git a/src/cockpit/389-console/src/lib/plugins/usn.jsx b/src/cockpit/389-console/src/lib/plugins/usn.jsx index e587755..a1080c6 100644 --- a/src/cockpit/389-console/src/lib/plugins/usn.jsx +++ b/src/cockpit/389-console/src/lib/plugins/usn.jsx @@ -20,7 +20,11 @@ import "../../css/ds.css"; class USN extends React.Component { componentWillMount() { - this.updateSwitch(); + if (this.props.wasActiveList.includes(5)) { + if (this.state.firstLoad) { + this.updateSwitch(); + } + } } constructor(props) { @@ -33,6 +37,7 @@ class USN extends React.Component { this.handleFieldChange = this.handleFieldChange.bind(this); this.state = { + firstLoad: true, globalMode: false, disableSwitch: false, cleanupModalShow: false, @@ -88,6 +93,10 @@ class USN extends React.Component { } updateSwitch() { + this.setState({ + firstLoad: false, + disableSwitch: true + }); const cmd = [ "dsconf", "-j", @@ -96,10 +105,6 @@ class USN extends React.Component { "get", "nsslapd-entryusn-global" ]; - - this.setState({ - disableSwitch: true - }); this.props.toggleLoadingHandler(); log_cmd("updateSwitch", "Get global USN status", cmd); cockpit diff --git a/src/cockpit/389-console/src/lib/server/accessLog.jsx b/src/cockpit/389-console/src/lib/server/accessLog.jsx index 85c5623..3dcf7e8 100644 --- a/src/cockpit/389-console/src/lib/server/accessLog.jsx +++ b/src/cockpit/389-console/src/lib/server/accessLog.jsx @@ -1,6 +1,5 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController } from "../notifications.jsx"; import { log_cmd } from "../tools.jsx"; import { Button, @@ -16,6 +15,7 @@ import { Spinner, TabContainer, TabContent, + noop, TabPane, } from "patternfly-react"; import "../../css/ds.css"; @@ -61,19 +61,16 @@ export class ServerAccessLog extends React.Component { loading: false, loaded: false, activeKey: 1, - notifications: [], saveSettingsDisabled: true, saveRotationDisabled: true, saveExpDisabled: true, attrs: this.props.attrs, }; - this.addNotification = this.addNotification.bind(this); this.handleChange = this.handleChange.bind(this); this.handleNavSelect = this.handleNavSelect.bind(this); this.loadConfig = this.loadConfig.bind(this); this.reloadConfig = this.reloadConfig.bind(this); - this.removeNotification = this.removeNotification.bind(this); this.saveConfig = this.saveConfig.bind(this); } @@ -92,29 +89,6 @@ export class ServerAccessLog extends React.Component { this.setState({ activeKey: key }); } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - handleChange(e, nav_tab) { let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; let attr = e.target.id; @@ -209,7 +183,7 @@ export class ServerAccessLog extends React.Component { this.setState({ loading: false }); - this.addNotification( + this.props.addNotification( "success", "Successfully updated Access Log settings" ); @@ -220,7 +194,7 @@ export class ServerAccessLog extends React.Component { this.setState({ loading: false }); - this.addNotification( + this.props.addNotification( "error", `Error saving Access Log settings - ${errMsg.desc}` ); @@ -308,7 +282,7 @@ export class ServerAccessLog extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading Access Log configuration - ${errMsg.desc}` ); @@ -728,10 +702,6 @@ export class ServerAccessLog extends React.Component { return (
- @@ -753,11 +723,13 @@ export class ServerAccessLog extends React.Component { // Property types and defaults ServerAccessLog.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string, attrs: PropTypes.object, }; ServerAccessLog.defaultProps = { + addNotification: noop, serverId: "", attrs: {}, }; diff --git a/src/cockpit/389-console/src/lib/server/auditLog.jsx b/src/cockpit/389-console/src/lib/server/auditLog.jsx index 88ba26a..623231b 100644 --- a/src/cockpit/389-console/src/lib/server/auditLog.jsx +++ b/src/cockpit/389-console/src/lib/server/auditLog.jsx @@ -1,6 +1,5 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController } from "../notifications.jsx"; import { log_cmd } from "../tools.jsx"; import { Button, @@ -16,6 +15,7 @@ import { Spinner, TabContainer, TabContent, + noop, TabPane, } from "patternfly-react"; import "../../css/ds.css"; @@ -50,19 +50,16 @@ export class ServerAuditLog extends React.Component { loading: false, loaded: false, activeKey: 1, - notifications: [], saveSettingsDisabled: true, saveRotationDisabled: true, saveExpDisabled: true, attrs: this.props.attrs, }; - this.addNotification = this.addNotification.bind(this); this.handleChange = this.handleChange.bind(this); this.handleNavSelect = this.handleNavSelect.bind(this); this.loadConfig = this.loadConfig.bind(this); this.reloadConfig = this.reloadConfig.bind(this); - this.removeNotification = this.removeNotification.bind(this); this.saveConfig = this.saveConfig.bind(this); } @@ -81,29 +78,6 @@ export class ServerAuditLog extends React.Component { this.setState({ activeKey: key }); } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - handleChange(e, nav_tab) { let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; let attr = e.target.id; @@ -183,7 +157,7 @@ export class ServerAuditLog extends React.Component { this.setState({ loading: false }); - this.addNotification( + this.props.addNotification( "success", "Successfully updated Audit Log settings" ); @@ -194,7 +168,7 @@ export class ServerAuditLog extends React.Component { this.setState({ loading: false }); - this.addNotification( + this.props.addNotification( "error", `Error saving Audit Log settings - ${errMsg.desc}` ); @@ -303,7 +277,7 @@ export class ServerAuditLog extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading Audit Log configuration - ${errMsg.desc}` ); @@ -588,10 +562,6 @@ export class ServerAuditLog extends React.Component { return (
- @@ -613,11 +583,13 @@ export class ServerAuditLog extends React.Component { // Property types and defaults ServerAuditLog.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string, attrs: PropTypes.object, }; ServerAuditLog.defaultProps = { + addNotification: noop, serverId: "", attrs: {}, }; diff --git a/src/cockpit/389-console/src/lib/server/auditfailLog.jsx b/src/cockpit/389-console/src/lib/server/auditfailLog.jsx index e7b88d2..caf49c7 100644 --- a/src/cockpit/389-console/src/lib/server/auditfailLog.jsx +++ b/src/cockpit/389-console/src/lib/server/auditfailLog.jsx @@ -1,6 +1,5 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController } from "../notifications.jsx"; import { log_cmd } from "../tools.jsx"; import { Button, @@ -16,6 +15,7 @@ import { Spinner, TabContainer, TabContent, + noop, TabPane, } from "patternfly-react"; import "../../css/ds.css"; @@ -50,19 +50,16 @@ export class ServerAuditFailLog extends React.Component { loading: false, loaded: false, activeKey: 1, - notifications: [], saveSettingsDisabled: true, saveRotationDisabled: true, saveExpDisabled: true, attrs: this.props.attrs, }; - this.addNotification = this.addNotification.bind(this); this.handleChange = this.handleChange.bind(this); this.handleNavSelect = this.handleNavSelect.bind(this); this.loadConfig = this.loadConfig.bind(this); this.reloadConfig = this.reloadConfig.bind(this); - this.removeNotification = this.removeNotification.bind(this); this.saveConfig = this.saveConfig.bind(this); } @@ -81,29 +78,6 @@ export class ServerAuditFailLog extends React.Component { this.setState({ activeKey: key }); } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - handleChange(e, nav_tab) { let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; let attr = e.target.id; @@ -183,7 +157,7 @@ export class ServerAuditFailLog extends React.Component { this.setState({ loading: false }); - this.addNotification( + this.props.addNotification( "success", "Successfully updated Audit Failure Log settings" ); @@ -194,7 +168,7 @@ export class ServerAuditFailLog extends React.Component { this.setState({ loading: false }); - this.addNotification( + this.props.addNotification( "error", `Error saving Audit Failure Log settings - ${errMsg.desc}` ); @@ -259,7 +233,7 @@ export class ServerAuditFailLog extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading Audit Failure Log configuration - ${errMsg.desc}` ); @@ -586,10 +560,6 @@ export class ServerAuditFailLog extends React.Component { return (
- @@ -611,11 +581,13 @@ export class ServerAuditFailLog extends React.Component { // Property types and defaults ServerAuditFailLog.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string, attrs: PropTypes.object, }; ServerAuditFailLog.defaultProps = { + addNotification: noop, serverId: "", attrs: {}, }; diff --git a/src/cockpit/389-console/src/lib/server/errorLog.jsx b/src/cockpit/389-console/src/lib/server/errorLog.jsx index 6416253..b21d005 100644 --- a/src/cockpit/389-console/src/lib/server/errorLog.jsx +++ b/src/cockpit/389-console/src/lib/server/errorLog.jsx @@ -1,6 +1,5 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController } from "../notifications.jsx"; import { log_cmd } from "../tools.jsx"; import { Button, @@ -16,6 +15,7 @@ import { Spinner, TabContainer, TabContent, + noop, TabPane, } from "patternfly-react"; import "../../css/ds.css"; @@ -70,19 +70,16 @@ export class ServerErrorLog extends React.Component { loading: false, loaded: false, activeKey: 1, - notifications: [], saveSettingsDisabled: true, saveRotationDisabled: true, saveExpDisabled: true, attrs: this.props.attrs, }; - this.addNotification = this.addNotification.bind(this); this.handleChange = this.handleChange.bind(this); this.handleNavSelect = this.handleNavSelect.bind(this); this.loadConfig = this.loadConfig.bind(this); this.reloadConfig = this.reloadConfig.bind(this); - this.removeNotification = this.removeNotification.bind(this); this.saveConfig = this.saveConfig.bind(this); } @@ -101,29 +98,6 @@ export class ServerErrorLog extends React.Component { this.setState({ activeKey: key }); } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - handleChange(e, nav_tab) { let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; let attr = e.target.id; @@ -218,7 +192,7 @@ export class ServerErrorLog extends React.Component { this.setState({ loading: false }); - this.addNotification( + this.props.addNotification( "success", "Successfully updated Error Log settings" ); @@ -229,7 +203,7 @@ export class ServerErrorLog extends React.Component { this.setState({ loading: false }); - this.addNotification( + this.props.addNotification( "error", `Error saving Error Log settings - ${errMsg.desc}` ); @@ -416,7 +390,7 @@ export class ServerErrorLog extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading Error Log configuration - ${errMsg.desc}` ); @@ -921,10 +895,6 @@ export class ServerErrorLog extends React.Component { return (
- @@ -946,11 +916,13 @@ export class ServerErrorLog extends React.Component { // Property types and defaults ServerErrorLog.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string, attrs: PropTypes.object, }; ServerErrorLog.defaultProps = { + addNotification: noop, serverId: "", attrs: {}, }; diff --git a/src/cockpit/389-console/src/lib/server/ldapi.jsx b/src/cockpit/389-console/src/lib/server/ldapi.jsx index 904689f..42dee17 100644 --- a/src/cockpit/389-console/src/lib/server/ldapi.jsx +++ b/src/cockpit/389-console/src/lib/server/ldapi.jsx @@ -1,6 +1,5 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController } from "../notifications.jsx"; import { log_cmd } from "../tools.jsx"; import { Button, @@ -11,6 +10,7 @@ import { FormControl, Icon, Row, + noop, Spinner, } from "patternfly-react"; import "../../css/ds.css"; @@ -31,15 +31,12 @@ export class ServerLDAPI extends React.Component { this.state = { loading: false, loaded: false, - notifications: [], saveDisabled: true, attrs: this.props.attrs, // settings }; - this.removeNotification = this.removeNotification.bind(this); - this.addNotification = this.addNotification.bind(this); this.handleChange = this.handleChange.bind(this); this.loadConfig = this.loadConfig.bind(this); this.saveConfig = this.saveConfig.bind(this); @@ -56,29 +53,6 @@ export class ServerLDAPI extends React.Component { this.props.enableTree(); } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - handleChange(e) { let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; let attr = e.target.id; @@ -166,7 +140,7 @@ export class ServerLDAPI extends React.Component { this.setState({ loading: false }); - this.addNotification( + this.props.addNotification( "success", "Successfully updated LDAPI configuration" ); @@ -177,7 +151,7 @@ export class ServerLDAPI extends React.Component { this.setState({ loading: false }); - this.addNotification( + this.props.addNotification( "error", `Error updating LDAPI configuration - ${errMsg.desc}` ); @@ -296,10 +270,6 @@ export class ServerLDAPI extends React.Component { return (
- @@ -321,11 +291,13 @@ export class ServerLDAPI extends React.Component { // Property types and defaults ServerLDAPI.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string, attrs: PropTypes.object, }; ServerLDAPI.defaultProps = { + addNotification: noop, serverId: "", attrs: {}, }; diff --git a/src/cockpit/389-console/src/lib/server/sasl.jsx b/src/cockpit/389-console/src/lib/server/sasl.jsx index 7dff66f..e58a660 100644 --- a/src/cockpit/389-console/src/lib/server/sasl.jsx +++ b/src/cockpit/389-console/src/lib/server/sasl.jsx @@ -1,6 +1,6 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController, DoubleConfirmModal } from "../notifications.jsx"; +import { DoubleConfirmModal } from "../notifications.jsx"; import { log_cmd } from "../tools.jsx"; import { Button, @@ -27,7 +27,6 @@ export class ServerSASL extends React.Component { loaded: false, activeKey: 1, errObj: {}, - notifications: [], saveDisabled: true, supportedMechs: [], @@ -52,8 +51,6 @@ export class ServerSASL extends React.Component { modalChecked: false, }; - this.removeNotification = this.removeNotification.bind(this); - this.addNotification = this.addNotification.bind(this); this.handleChange = this.handleChange.bind(this); this.handleModalChange = this.handleModalChange.bind(this); this.handleModalAddChange = this.handleModalAddChange.bind(this); @@ -83,41 +80,18 @@ export class ServerSASL extends React.Component { this.props.enableTree(); } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - handleTestRegex() { let test_string = this.state.saslTestText; let regex = this.state.saslMapRegex; let cleaned_regex = regex.replace(/\\\(/g, '(').replace(/\\\)/g, ')'); let sasl_regex = RegExp(cleaned_regex); if (sasl_regex.test(test_string)) { - this.addNotification( + this.props.addNotification( "success", "The test string matches the Regular Expression" ); } else { - this.addNotification( + this.props.addNotification( "warning", "The test string does not match the Regular Expression" ); @@ -449,7 +423,7 @@ export class ServerSASL extends React.Component { .done(content => { this.loadConfig(); this.closeMapping(); - this.addNotification( + this.props.addNotification( "success", "Successfully create new SASL Mapping" ); @@ -457,7 +431,7 @@ export class ServerSASL extends React.Component { .fail(err => { let errMsg = JSON.parse(err); this.loadConfig(); - this.addNotification( + this.props.addNotification( "error", `Error creating new SASL Mapping - ${errMsg.desc}` ); @@ -505,7 +479,7 @@ export class ServerSASL extends React.Component { .done(content => { this.closeMapping(); this.loadConfig(); - this.addNotification( + this.props.addNotification( "success", "Successfully updated SASL Mapping" ); @@ -514,7 +488,7 @@ export class ServerSASL extends React.Component { let errMsg = JSON.parse(err); this.closeMapping(); this.loadConfig(); - this.addNotification( + this.props.addNotification( "error", `Error updating SASL Mapping - ${errMsg.desc}` ); @@ -524,7 +498,7 @@ export class ServerSASL extends React.Component { let errMsg = JSON.parse(err); this.loadConfig(); this.closeMapping(); - this.addNotification( + this.props.addNotification( "error", `Error replacing SASL Mapping - ${errMsg.desc}` ); @@ -556,7 +530,7 @@ export class ServerSASL extends React.Component { .done(content => { this.closeConfirmDelete(); this.loadConfig(); - this.addNotification( + this.props.addNotification( "success", "Successfully deleted SASL Mapping" ); @@ -565,7 +539,7 @@ export class ServerSASL extends React.Component { let errMsg = JSON.parse(err); this.loadConfig(); this.closeConfirmDelete(); - this.addNotification( + this.props.addNotification( "error", `Error deleting SASL Mapping - ${errMsg.desc}` ); @@ -604,7 +578,7 @@ export class ServerSASL extends React.Component { .spawn(cmd, {superuser: true, "err": "message"}) .done(content => { this.loadConfig(); - this.addNotification( + this.props.addNotification( "success", "Successfully updated SASL configuration. These " + "changes require the server to be restarted to take effect." @@ -613,7 +587,7 @@ export class ServerSASL extends React.Component { .fail(err => { let errMsg = JSON.parse(err); this.loadConfig(); - this.addNotification( + this.props.addNotification( "error", `Error updating SASL configuration - ${errMsg.desc}` ); @@ -748,10 +722,6 @@ export class ServerSASL extends React.Component { return (
- {body} ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - handleNavSelect(key) { this.setState({ activeKey: key }); } @@ -440,7 +414,7 @@ export class ServerSettings extends React.Component { .spawn(cmd, {superuser: true, "err": "message"}) .done(content => { this.reloadRootDN(); - this.addNotification( + this.props.addNotification( "success", "Successfully updated Directory Manager configuration" ); @@ -448,7 +422,7 @@ export class ServerSettings extends React.Component { .fail(err => { let errMsg = JSON.parse(err); this.reloadRootDN(); - this.addNotification( + this.props.addNotification( "error", `Error updating Directory Manager configuration - ${errMsg.desc}` ); @@ -490,7 +464,7 @@ export class ServerSettings extends React.Component { this.setState({ rootDNReloading: false, }); - this.addNotification( + this.props.addNotification( "error", `Error reloading Directory Manager configuration - ${errMsg.desc}` ); @@ -522,7 +496,7 @@ export class ServerSettings extends React.Component { .spawn(cmd, {superuser: true, "err": "message"}) .done(content => { this.reloadDiskMonitoring(); - this.addNotification( + this.props.addNotification( "success", "Successfully updated Disk Monitoring configuration" ); @@ -530,7 +504,7 @@ export class ServerSettings extends React.Component { .fail(err => { let errMsg = JSON.parse(err); this.reloadDiskMonitoring(); - this.addNotification( + this.props.addNotification( "error", `Error updating Disk Monitoring configuration - ${errMsg.desc}` ); @@ -582,7 +556,7 @@ export class ServerSettings extends React.Component { this.setState({ diskMonReloading: false, }); - this.addNotification( + this.props.addNotification( "error", `Error reloading Disk Monitoring configuration - ${errMsg.desc}` ); @@ -613,7 +587,7 @@ export class ServerSettings extends React.Component { .spawn(cmd, {superuser: true, "err": "message"}) .done(content => { this.reloadAdvanced(); - this.addNotification( + this.props.addNotification( "success", "Successfully updated Advanced configuration" ); @@ -621,7 +595,7 @@ export class ServerSettings extends React.Component { .fail(err => { let errMsg = JSON.parse(err); this.reloadAdvanced(); - this.addNotification( + this.props.addNotification( "error", `Error updating Advanced configuration - ${errMsg.desc}` ); @@ -719,7 +693,7 @@ export class ServerSettings extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading Advanced configuration - ${errMsg.desc}` ); @@ -748,7 +722,7 @@ export class ServerSettings extends React.Component { .done(content => { // Continue with the next mod this.reloadConfig(); - this.addNotification( + this.props.addNotification( "success", "Successfully updated server configuration. These " + "changes require the server to be restarted to take effect." @@ -757,7 +731,7 @@ export class ServerSettings extends React.Component { .fail(err => { let errMsg = JSON.parse(err); this.reloadConfig(); - this.addNotification( + this.props.addNotification( "error", `Error updating server configuration - ${errMsg.desc}` ); @@ -809,7 +783,7 @@ export class ServerSettings extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error reloading server configuration - ${errMsg.desc}` ); @@ -1325,10 +1299,6 @@ export class ServerSettings extends React.Component { return (
- {body}
); @@ -1338,11 +1308,13 @@ export class ServerSettings extends React.Component { // Property types and defaults ServerSettings.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string, attrs: PropTypes.object, }; ServerSettings.defaultProps = { + addNotification: noop, serverId: "", attrs: {}, }; diff --git a/src/cockpit/389-console/src/lib/server/tuning.jsx b/src/cockpit/389-console/src/lib/server/tuning.jsx index f688897..6f78958 100644 --- a/src/cockpit/389-console/src/lib/server/tuning.jsx +++ b/src/cockpit/389-console/src/lib/server/tuning.jsx @@ -1,6 +1,5 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController } from "../notifications.jsx"; import CustomCollapse from "../customCollapse.jsx"; import { log_cmd } from "../tools.jsx"; import { @@ -12,6 +11,7 @@ import { Icon, Checkbox, Row, + noop, Spinner, } from "patternfly-react"; import PropTypes from "prop-types"; @@ -44,14 +44,11 @@ export class ServerTuning extends React.Component { loading: false, loaded: false, activeKey: 1, - notifications: [], saveDisabled: true, errObj: {}, attrs: this.props.attrs, }; - this.removeNotification = this.removeNotification.bind(this); - this.addNotification = this.addNotification.bind(this); this.handleChange = this.handleChange.bind(this); this.loadConfig = this.loadConfig.bind(this); this.saveConfig = this.saveConfig.bind(this); @@ -68,29 +65,6 @@ export class ServerTuning extends React.Component { this.props.enableTree(); } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - handleChange(e) { let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; let attr = e.target.id; @@ -218,7 +192,7 @@ export class ServerTuning extends React.Component { .spawn(cmd, {superuser: true, "err": "message"}) .done(content => { this.loadConfig(1); - this.addNotification( + this.props.addNotification( "success", "Successfully updated Advanced configuration" ); @@ -226,7 +200,7 @@ export class ServerTuning extends React.Component { .fail(err => { let errMsg = JSON.parse(err); this.loadConfig(1); - this.addNotification( + this.props.addNotification( "error", `Error updating Advanced configuration - ${errMsg.desc}` ); @@ -533,10 +507,6 @@ export class ServerTuning extends React.Component { return (
- {body}
); @@ -546,11 +516,13 @@ export class ServerTuning extends React.Component { // Property types and defaults ServerTuning.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string, attrs: PropTypes.object, }; ServerTuning.defaultProps = { + addNotification: noop, serverId: "", attrs: {}, }; diff --git a/src/cockpit/389-console/src/monitor.jsx b/src/cockpit/389-console/src/monitor.jsx index 1c97497..a155f49 100644 --- a/src/cockpit/389-console/src/monitor.jsx +++ b/src/cockpit/389-console/src/monitor.jsx @@ -1,6 +1,5 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController } from "./lib/notifications.jsx"; import { log_cmd } from "./lib/tools.jsx"; import { TreeView, @@ -8,6 +7,7 @@ import { Row, Col, Icon, + noop, ControlLabel } from "patternfly-react"; import PropTypes from "prop-types"; @@ -31,6 +31,7 @@ export class Monitor extends React.Component { constructor(props) { super(props); this.state = { + firstLoad: true, nodes: [], node_name: "", node_text: "", @@ -41,7 +42,6 @@ export class Monitor extends React.Component { serverData: {}, disks: [], loadingMsg: "", - notifications: [], disableTree: false, // Suffix suffixLoading: false, @@ -110,8 +110,6 @@ export class Monitor extends React.Component { this.sev_all_info = [sev_warn, sev_notice, sev_info, sev_debug]; // Bindings - this.addNotification = this.addNotification.bind(this); - this.removeNotification = this.removeNotification.bind(this); this.loadSuffixTree = this.loadSuffixTree.bind(this); this.enableTree = this.enableTree.bind(this); this.update_tree_nodes = this.update_tree_nodes.bind(this); @@ -155,40 +153,24 @@ export class Monitor extends React.Component { this.handleSevChange = this.handleSevChange.bind(this); } - componentDidMount() { - this.loadMonitor(); - } - componentDidUpdate(prevProps) { - if (this.props.serverId !== prevProps.serverId) { - this.loadSuffixTree(false); - } - } - - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, + if (this.props.wasActiveList.includes(6)) { + if (this.state.firstLoad) { + this.loadMonitor(); + } else { + if (this.props.serverId !== prevProps.serverId) { + this.loadSuffixTree(false); } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); + } + } } loadSuffixTree(fullReset) { + if (this.state.firstLoad) { + this.setState({ + firstLoad: false + }); + } const cmd = [ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", "backend", "get-tree", @@ -1207,7 +1189,7 @@ export class Monitor extends React.Component { suffix={this.state.replSuffix} serverId={this.props.serverId} data={this.state[this.state.replSuffix]} - addNotification={this.addNotification} + addNotification={this.props.addNotification} reloadAgmts={this.reloadReplAgmts} reloadWinsyncAgmts={this.reloadReplWinsyncAgmts} reloadConflicts={this.loadConflicts} @@ -1261,10 +1243,6 @@ export class Monitor extends React.Component { } monitorPage =
-
@@ -1305,9 +1283,11 @@ export class Monitor extends React.Component { // Property types and defaults Monitor.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string }; Monitor.defaultProps = { + addNotification: noop, serverId: "" }; diff --git a/src/cockpit/389-console/src/plugins.jsx b/src/cockpit/389-console/src/plugins.jsx index 342a19c..ae24945 100644 --- a/src/cockpit/389-console/src/plugins.jsx +++ b/src/cockpit/389-console/src/plugins.jsx @@ -2,7 +2,7 @@ import cockpit from "cockpit"; import React from "react"; import PropTypes from "prop-types"; import { log_cmd } from "./lib/tools.jsx"; -import { Col, Row, Tab, Nav, NavItem, Spinner } from "patternfly-react"; +import { Col, Row, Tab, Nav, NavItem, noop, Spinner } from "patternfly-react"; import PluginEditModal from "./lib/plugins/pluginModal.jsx"; import { PluginTable } from "./lib/plugins/pluginTables.jsx"; import AccountPolicy from "./lib/plugins/accountPolicy.jsx"; @@ -18,25 +18,20 @@ import RetroChangelog from "./lib/plugins/retroChangelog.jsx"; import RootDNAccessControl from "./lib/plugins/rootDNAccessControl.jsx"; import USN from "./lib/plugins/usn.jsx"; import WinSync from "./lib/plugins/winsync.jsx"; -import { NotificationController } from "./lib/notifications.jsx"; import "./css/ds.css"; var cmd; export class Plugins extends React.Component { - componentWillMount() { - this.pluginList(); - this.setState(prevState => ({ - pluginTabs: { - ...prevState.pluginTabs, - basicConfig: true - } - })); - } - componentDidUpdate(prevProps) { - if (this.props.serverId !== prevProps.serverId) { - this.pluginList(); + if (this.props.wasActiveList.includes(5)) { + if (this.state.firstLoad) { + this.pluginList(); + } else { + if (this.props.serverId !== prevProps.serverId) { + this.pluginList(); + } + } } } @@ -48,14 +43,12 @@ export class Plugins extends React.Component { this.openPluginModal = this.openPluginModal.bind(this); this.closePluginModal = this.closePluginModal.bind(this); this.pluginList = this.pluginList.bind(this); - this.removeNotification = this.removeNotification.bind(this); - this.addNotification = this.addNotification.bind(this); this.onChangeTab = this.onChangeTab.bind(this); this.savePlugin = this.savePlugin.bind(this); this.toggleLoading = this.toggleLoading.bind(this); this.state = { - notifications: [], + firstLoad: true, loading: false, showPluginModal: false, currentPluginTab: "", @@ -81,29 +74,6 @@ export class Plugins extends React.Component { this.setState({ currentPluginTab: event.target.value }); } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - handleFieldChange(e) { this.setState({ [e.target.id]: e.target.value @@ -168,6 +138,15 @@ export class Plugins extends React.Component { } pluginList() { + if (this.state.firstLoad) { + this.setState(prevState => ({ + firstLoad: false, + pluginTabs: { + ...prevState.pluginTabs, + basicConfig: true + } + })); + } cmd = [ "dsconf", "-j", @@ -239,7 +218,10 @@ export class Plugins extends React.Component { .done(content => { console.info("savePlugin", "Result", content); basicPluginSuccess = true; - this.addNotification("success", `Plugin ${data.name} was successfully modified`); + this.props.addNotification( + "success", + `Plugin ${data.name} was successfully modified` + ); this.pluginList(); this.closePluginModal(); this.toggleLoading(); @@ -249,7 +231,7 @@ export class Plugins extends React.Component { if (errMsg.desc.indexOf("nothing to set") >= 0) { nothingToSetErr = true; } else { - this.addNotification( + this.props.addNotification( "error", `${errMsg.desc} error during ${data.name} modification` ); @@ -273,7 +255,7 @@ export class Plugins extends React.Component { .done(content => { // Notify success only one time if (!basicPluginSuccess) { - this.addNotification( + this.props.addNotification( "success", `Plugin ${data.name} was successfully modified` ); @@ -289,13 +271,13 @@ export class Plugins extends React.Component { errMsg.desc.indexOf("nothing to set") < 0 ) { if (basicPluginSuccess) { - this.addNotification( + this.props.addNotification( "success", `Plugin ${data.name} was successfully modified` ); this.pluginList(); } - this.addNotification( + this.props.addNotification( "error", `${errMsg.desc} error during ${data.name} modification` ); @@ -324,7 +306,7 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} /> ) @@ -337,8 +319,9 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} + wasActiveList={this.props.wasActiveList} /> ) }, @@ -350,8 +333,9 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} + wasActiveList={this.props.wasActiveList} /> ) }, @@ -363,8 +347,9 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} + wasActiveList={this.props.wasActiveList} /> ) }, @@ -376,8 +361,9 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} + wasActiveList={this.props.wasActiveList} /> ) }, @@ -389,8 +375,9 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} + wasActiveList={this.props.wasActiveList} /> ) }, @@ -402,7 +389,7 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} /> ) @@ -415,8 +402,9 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} + wasActiveList={this.props.wasActiveList} /> ) }, @@ -428,7 +416,7 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} /> ) @@ -441,8 +429,9 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} + wasActiveList={this.props.wasActiveList} /> ) }, @@ -454,8 +443,9 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} + wasActiveList={this.props.wasActiveList} /> ) }, @@ -467,7 +457,7 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} /> ) @@ -480,18 +470,15 @@ export class Plugins extends React.Component { serverId={this.props.serverId} savePluginHandler={this.savePlugin} pluginListHandler={this.pluginList} - addNotification={this.addNotification} + addNotification={this.props.addNotification} toggleLoadingHandler={this.toggleLoading} + wasActiveList={this.props.wasActiveList} /> ) } }; return (
- ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - update_tree_nodes() { // Set title to the text value of each suffix node. We need to do this // so we can read long suffixes in the UI tree div. This is the last @@ -689,7 +666,7 @@ export class Replication extends React.Component { .fail(err => { let errMsg = JSON.parse(err); if (errMsg.desc != "No such object") { - this.addNotification( + this.props.addNotification( "error", `Error loading suffix RUV - ${errMsg.desc}` ); @@ -922,7 +899,7 @@ export class Replication extends React.Component { .fail(err => { let errMsg = JSON.parse(err); if (errMsg.desc != "No such object") { - this.addNotification( + this.props.addNotification( "error", `Error loading suffix RUV - ${errMsg.desc}` ); @@ -935,7 +912,7 @@ export class Replication extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading winsync agreements - ${errMsg.desc}` ); @@ -947,7 +924,7 @@ export class Replication extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error loading replication agreements configuration - ${errMsg.desc}` ); @@ -987,7 +964,7 @@ export class Replication extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Failed to get attributes - ${errMsg.desc}` ); @@ -1033,7 +1010,7 @@ export class Replication extends React.Component { suffix={this.state.node_name} role={this.state[this.state.node_name].role} data={this.state[this.state.node_name]} - addNotification={this.addNotification} + addNotification={this.props.addNotification} agmtRows={this.state[this.state.node_name].agmtRows} winsyncRows={this.state[this.state.node_name].winsyncRows} ruvRows={this.state[this.state.node_name].ruvRows} @@ -1059,7 +1036,7 @@ export class Replication extends React.Component { suffix={this.state.node_name} role="" data="" - addNotification={this.addNotification} + addNotification={this.props.addNotification} disableWSAgmtTable={this.state.disableWSAgmtTable} disableAgmtTable={this.state.disableAgmtTable} reloadAgmts={this.reloadAgmts} @@ -1087,7 +1064,7 @@ export class Replication extends React.Component { clCompactInt={this.state.clCompactInt} clTrimInt={this.state.clTrimInt} clEncrypt={this.state.clEncrypt} - addNotification={this.addNotification} + addNotification={this.props.addNotification} enableTree={this.enableTree} reload={this.reloadChangelog} loading={this.state.clLoading} @@ -1096,10 +1073,6 @@ export class Replication extends React.Component { } repl_page =
-
@@ -1138,10 +1111,12 @@ export class Replication extends React.Component { // Property types and defaults Replication.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string }; Replication.defaultProps = { + addNotification: noop, serverId: "" }; diff --git a/src/cockpit/389-console/src/schema.jsx b/src/cockpit/389-console/src/schema.jsx index 7e6ade6..0078e47 100644 --- a/src/cockpit/389-console/src/schema.jsx +++ b/src/cockpit/389-console/src/schema.jsx @@ -1,6 +1,5 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController } from "./lib/notifications.jsx"; import { log_cmd, searchFilter } from "./lib/tools.jsx"; import { ObjectClassesTable, @@ -16,27 +15,30 @@ import { TabContent, TabPane, Spinner, + noop, Button } from "patternfly-react"; import PropTypes from "prop-types"; import "./css/ds.css"; export class Schema extends React.Component { - componentWillMount() { - this.loadSyntaxesFirst(); - } - componentDidUpdate(prevProps) { - if (this.props.serverId !== prevProps.serverId) { - this.loadSyntaxesFirst(); + if (this.props.wasActiveList.includes(4)) { + if (this.state.firstLoad) { + this.loadSyntaxesFirst(); + } else { + if (this.props.serverId !== prevProps.serverId) { + this.loadSyntaxesFirst(); + } + } } } constructor(props) { super(props); this.state = { + firstLoad: true, loading: false, - notifications: [], activeKey: 1, objectclassRows: [], @@ -84,8 +86,6 @@ export class Schema extends React.Component { }; this.handleFieldChange = this.handleFieldChange.bind(this); - this.addNotification = this.addNotification.bind(this); - this.removeNotification = this.removeNotification.bind(this); this.handleNavSelect = this.handleNavSelect.bind(this); this.handleTypeaheadChange = this.handleTypeaheadChange.bind(this); this.loadSchemaData = this.loadSchemaData.bind(this); @@ -138,6 +138,12 @@ export class Schema extends React.Component { } loadSyntaxesFirst() { + if (this.state.firstLoad) { + this.setState({ + firstLoad: false + }); + } + this.toggleLoading("allSchema"); let cmd = [ "dsconf", "-j", @@ -172,9 +178,6 @@ export class Schema extends React.Component { "schema", "list" ]; - if (initialLoading) { - this.toggleLoading("allSchema"); - } log_cmd("loadSchemaData", "Get schema objects in one batch", cmd); cockpit .spawn(cmd, { superuser: true, err: "message" }) @@ -389,13 +392,13 @@ export class Schema extends React.Component { }) .done(content => { console.info("deleteObjectclass", "Result", content); - this.addNotification("success", `ObjectClass ${name} was successfully deleted`); + this.props.addNotification("success", `ObjectClass ${name} was successfully deleted`); this.loadSchemaData(); this.toggleLoading("ocTable"); }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error during ObjectClass removal operation - ${errMsg.desc}` ); @@ -415,7 +418,7 @@ export class Schema extends React.Component { cmdOperationObjectclass(action) { const { ocName, ocDesc, ocOID, ocParent, ocKind, ocMust, ocMay } = this.state; if (ocName == "") { - this.addNotification("warning", "ObjectClass Name is required."); + this.props.addNotification("warning", "ObjectClass Name is required."); } else { let cmd = [ "dsconf", @@ -461,7 +464,7 @@ export class Schema extends React.Component { }) .done(content => { console.info("cmdOperationObjectclass", "Result", content); - this.addNotification( + this.props.addNotification( "success", `ObjectClass ${ocName} - ${action} operation was successfull` ); @@ -471,7 +474,7 @@ export class Schema extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error during the ObjectClass ${action} operation - ${errMsg.desc}` ); @@ -665,13 +668,13 @@ export class Schema extends React.Component { }) .done(content => { console.info("deleteAttribute", "Result", content); - this.addNotification("success", `Attribute ${name} was successfully deleted`); + this.props.addNotification("success", `Attribute ${name} was successfully deleted`); this.loadSchemaData(); this.toggleLoading("atTable"); }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error during Attribute removal operation - ${errMsg.desc}` ); @@ -705,7 +708,7 @@ export class Schema extends React.Component { } = this.state; if (atName == "" || atSyntax.length == 0) { - this.addNotification("warning", "Attribute Name and Syntax are required."); + this.props.addNotification("warning", "Attribute Name and Syntax are required."); } else { let cmd = [ "dsconf", @@ -774,7 +777,7 @@ export class Schema extends React.Component { }) .done(content => { console.info("cmdOperationAttribute", "Result", content); - this.addNotification( + this.props.addNotification( "success", `Attribute ${atName} - ${action} operation was successfull` ); @@ -784,7 +787,7 @@ export class Schema extends React.Component { }) .fail(err => { let errMsg = JSON.parse(err); - this.addNotification( + this.props.addNotification( "error", `Error during the Attribute ${action} operation - ${errMsg.desc}` ); @@ -795,29 +798,6 @@ export class Schema extends React.Component { } } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - handleNavSelect(key) { this.setState({ activeKey: key @@ -876,10 +856,6 @@ export class Schema extends React.Component { } else { schemaPage = (
-
({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - componentWillMount () { if (!this.state.loaded) { this.setState({securityEnabled: true}, this.setState({securityEnabled: false})); @@ -122,6 +97,12 @@ export class Security extends React.Component { this.props.enableTree(); } + componentDidUpdate(prevProps) { + if (this.props.serverId !== prevProps.serverId) { + this.loadSecurityConfig(); + } + } + loadSupportedCiphers () { const cmd = [ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", @@ -142,7 +123,7 @@ export class Security extends React.Component { if ('info' in errMsg) { msg = errMsg.desc + " - " + errMsg.info; } - this.addNotification( + this.props.addNotification( "error", `Error loading security configuration - ${msg}` ); @@ -169,7 +150,7 @@ export class Security extends React.Component { if ('info' in errMsg) { msg = errMsg.desc + " - " + errMsg.info; } - this.addNotification( + this.props.addNotification( "error", `Error loading security configuration - ${msg}` ); @@ -199,7 +180,7 @@ export class Security extends React.Component { if ('info' in errMsg) { msg = errMsg.desc + " - " + errMsg.info; } - this.addNotification( + this.props.addNotification( "error", `Error loading CA certificates - ${msg}` ); @@ -234,7 +215,7 @@ export class Security extends React.Component { if ('info' in errMsg) { msg = errMsg.desc + " - " + errMsg.info; } - this.addNotification( + this.props.addNotification( "error", `Error loading server certificates - ${msg}` ); @@ -265,7 +246,7 @@ export class Security extends React.Component { if ('info' in errMsg) { msg = errMsg.desc + " - " + errMsg.info; } - this.addNotification( + this.props.addNotification( "error", `Error loading security RSA configuration - ${msg}` ); @@ -363,7 +344,7 @@ export class Security extends React.Component { if ('info' in errMsg) { msg = errMsg.desc + " - " + errMsg.info; } - this.addNotification( + this.props.addNotification( "error", `Error loading security configuration - ${msg}` ); @@ -389,7 +370,7 @@ export class Security extends React.Component { showSecurityEnableModal: true, }); } else { - this.addNotification( + this.props.addNotification( "error", `There must be at least one server certificate present in the security database to enable security` ); @@ -430,7 +411,7 @@ export class Security extends React.Component { cockpit .spawn(cmd, { superuser: true, err: "message" }) .done(() => { - this.addNotification( + this.props.addNotification( "success", `Successfully enabled security. You must restart the server for this to take effect.` ); @@ -446,7 +427,7 @@ export class Security extends React.Component { if ('info' in errMsg) { msg = errMsg.desc + " - " + errMsg.info; } - this.addNotification( + this.props.addNotification( "error", `Error enabling security - ${msg}` ); @@ -466,7 +447,7 @@ export class Security extends React.Component { cockpit .spawn(cmd, { superuser: true, err: "message" }) .done(() => { - this.addNotification( + this.props.addNotification( "success", `Successfully disabled security. You must restart the server for this to take effect.` ); @@ -480,7 +461,7 @@ export class Security extends React.Component { if ('info' in errMsg) { msg = errMsg.desc + " - " + errMsg.info; } - this.addNotification( + this.props.addNotification( "error", `Error disabling security - ${msg}` ); @@ -499,7 +480,7 @@ export class Security extends React.Component { } if (sslMin > sslMax) { - this.addNotification( + this.props.addNotification( "error", `The TLS minimum version but be less than or equal to the TLS maximum version` ); @@ -571,7 +552,7 @@ export class Security extends React.Component { .spawn(cmd, {superuser: true, "err": "message"}) .done(content => { this.loadSecurityConfig(1); - this.addNotification( + this.props.addNotification( "success", msg ); @@ -589,7 +570,7 @@ export class Security extends React.Component { if ('info' in errMsg) { msg = errMsg.desc + " - " + errMsg.info; } - this.addNotification( + this.props.addNotification( "error", `Error updating security configuration - ${msg}` ); @@ -778,10 +759,6 @@ export class Security extends React.Component { securityPage =
- @@ -835,7 +812,7 @@ export class Security extends React.Component { serverId={this.props.serverId} CACerts={this.state.CACerts} ServerCerts={this.state.serverCerts} - addNotification={this.addNotification} + addNotification={this.props.addNotification} />
@@ -847,7 +824,7 @@ export class Security extends React.Component { supportedCiphers={this.state.supportedCiphers} cipherPref={this.state.cipherPref} enabledCiphers={this.state.enabledCiphers} - addNotification={this.addNotification} + addNotification={this.props.addNotification} />
@@ -898,10 +875,12 @@ export class Security extends React.Component { // Props and defaultProps Security.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string, }; Security.defaultProps = { + addNotification: noop, serverId: "", }; diff --git a/src/cockpit/389-console/src/server.jsx b/src/cockpit/389-console/src/server.jsx index 7ad9fea..a67ba40 100644 --- a/src/cockpit/389-console/src/server.jsx +++ b/src/cockpit/389-console/src/server.jsx @@ -1,11 +1,7 @@ import cockpit from "cockpit"; import React from "react"; -import { NotificationController } from "./lib/notifications.jsx"; import { log_cmd } from "./lib/tools.jsx"; -import { - TreeView, - Spinner, -} from "patternfly-react"; +import { TreeView, Spinner, noop } from "patternfly-react"; import PropTypes from "prop-types"; import { ServerSettings } from "./lib/server/settings.jsx"; import { ServerTuning } from "./lib/server/tuning.jsx"; @@ -18,88 +14,77 @@ import { ServerErrorLog } from "./lib/server/errorLog.jsx"; import { Security } from "./security.jsx"; const treeViewContainerStyles = { - width: '295px', + width: "295px" }; export class Server extends React.Component { constructor(props) { super(props); this.state = { + firstLoad: true, nodes: [], node_name: "", node_text: "", attrs: [], loaded: false, - disableTree: false, + disableTree: false }; - this.addNotification = this.addNotification.bind(this); - this.removeNotification = this.removeNotification.bind(this); this.loadTree = this.loadTree.bind(this); this.enableTree = this.enableTree.bind(this); this.selectNode = this.selectNode.bind(this); } - componentWillMount() { - this.loadConfig(); + componentDidUpdate(prevProps) { + if (this.props.wasActiveList.includes(1)) { + if (this.state.firstLoad) { + this.loadConfig(); + } else { + if (this.props.serverId !== prevProps.serverId) { + this.loadConfig(); + } + } + } } enableTree() { this.setState({ - disableTree: false, + disableTree: false }); } - loadConfig () { - let cmd = [ - "dsconf", "-j", this.props.serverId, "config", "get" - ]; + loadConfig() { + this.setState({ + loaded: false, + firstLoad: false + }); + let cmd = ["dsconf", "-j", this.props.serverId, "config", "get"]; log_cmd("loadConfig", "Load server configuration", cmd); cockpit .spawn(cmd, { superuser: true, err: "message" }) .done(content => { let config = JSON.parse(content); let attrs = config.attrs; - this.setState({ - loaded: true, - attrs: attrs - }, this.loadTree()); + this.setState( + { + loaded: true, + attrs: attrs + }, + this.loadTree() + ); }) .fail(err => { let errMsg = JSON.parse(err); this.setState({ - loaded: true, + loaded: true }); - this.addNotification( + this.props.addNotification( "error", `Error loading server configuration - ${errMsg.desc}` ); }); } - addNotification(type, message, timerdelay, persistent) { - this.setState(prevState => ({ - notifications: [ - ...prevState.notifications, - { - key: prevState.notifications.length + 1, - type: type, - persistent: persistent, - timerdelay: timerdelay, - message: message, - } - ] - })); - } - - removeNotification(notificationToRemove) { - this.setState({ - notifications: this.state.notifications.filter( - notification => notificationToRemove.key !== notification.key - ) - }); - } - loadTree() { let basicData = [ { @@ -107,7 +92,7 @@ export class Server extends React.Component { selectable: true, selected: true, icon: "pficon-settings", - state: {"expanded": true}, + state: { expanded: true }, id: "settings-config", nodes: [] }, @@ -144,42 +129,42 @@ export class Server extends React.Component { icon: "pficon-catalog", selectable: false, id: "logging-config", - state: {"expanded": true}, + state: { expanded: true }, nodes: [ { text: "Access Log", icon: "glyphicon glyphicon-book", selectable: true, id: "access-log-config", - type: "log", + type: "log" }, { text: "Audit Log", icon: "glyphicon glyphicon-book", selectable: true, id: "audit-log-config", - type: "log", + type: "log" }, { text: "Audit Failure Log", icon: "glyphicon glyphicon-book", selectable: true, id: "auditfail-log-config", - type: "log", + type: "log" }, { text: "Errors Log", icon: "glyphicon glyphicon-book", selectable: true, id: "error-log-config", - type: "log", - }, + type: "log" + } ] - }, + } ]; this.setState({ nodes: basicData, - node_name: this.state.node_name, + node_name: this.state.node_name }); } @@ -188,7 +173,7 @@ export class Server extends React.Component { return; } this.setState({ - disableTree: true, // Disable the tree to allow node to be fully loaded + disableTree: true // Disable the tree to allow node to be fully loaded }); this.setState(prevState => { @@ -196,7 +181,7 @@ export class Server extends React.Component { nodes: this.nodeSelector(prevState.nodes, selectedNode), node_name: selectedNode.id, node_text: selectedNode.text, - bename: "", + bename: "" }; }); } @@ -221,12 +206,13 @@ export class Server extends React.Component { render() { const { nodes } = this.state; - let serverPage = + let serverPage = (

Loading server configuration ...

-
; +
+ ); let server_element = ""; let disabled = "tree-view-container"; @@ -236,79 +222,85 @@ export class Server extends React.Component { if (this.state.loaded) { if (this.state.node_name == "settings-config" || this.state.node_name == "") { - server_element = + server_element = ( ; + /> + ); } else if (this.state.node_name == "tuning-config") { - server_element = + server_element = ( ; + /> + ); } else if (this.state.node_name == "sasl-config") { - server_element = - ; + server_element = ( + + ); } else if (this.state.node_name == "security-config") { - server_element = + server_element = ( ; + /> + ); } else if (this.state.node_name == "ldapi-config") { - server_element = + server_element = ( ; + /> + ); } else if (this.state.node_name == "access-log-config") { - server_element = + server_element = ( ; + /> + ); } else if (this.state.node_name == "audit-log-config") { - server_element = + server_element = ( ; + /> + ); } else if (this.state.node_name == "auditfail-log-config") { - server_element = + server_element = ( ; + /> + ); } else if (this.state.node_name == "error-log-config") { - server_element = + server_element = ( ; + /> + ); } - serverPage = + serverPage = (
-
-
+
-
- {server_element} -
+
{server_element}
-
; +
+ ); } - return ( -
- {serverPage} -
- ); + return
{serverPage}
; } } // Property types and defaults Server.propTypes = { + addNotification: PropTypes.func, serverId: PropTypes.string }; Server.defaultProps = { + addNotification: noop, serverId: "" }; diff --git a/src/cockpit/389-console/webpack.config.js b/src/cockpit/389-console/webpack.config.js index 3389135..972bc5a 100644 --- a/src/cockpit/389-console/webpack.config.js +++ b/src/cockpit/389-console/webpack.config.js @@ -26,7 +26,6 @@ var info = { files: [ "banner.html", "css", - "ds.js", "fonts", "images", "index.html", diff --git a/src/lib389/cli/dsctl b/src/lib389/cli/dsctl index a4a5f9c..f3bbabc 100755 --- a/src/lib389/cli/dsctl +++ b/src/lib389/cli/dsctl @@ -10,6 +10,7 @@ # # PYTHON_ARGCOMPLETE_OK +import json import argparse, argcomplete import logging import sys @@ -83,8 +84,11 @@ if __name__ == '__main__': if args.list: insts = get_instance_list() - for inst in insts: - print(inst) + if args.json: + print(json.dumps({"type": "result", "insts": insts}, indent=4)) + else: + for inst in insts: + print(inst) sys.exit(0) elif args.remove_all is not False: instance_remove_all(log, args) diff --git a/src/lib389/lib389/cli_ctl/instance.py b/src/lib389/lib389/cli_ctl/instance.py index bdeaa9f..d82c1a9 100644 --- a/src/lib389/lib389/cli_ctl/instance.py +++ b/src/lib389/lib389/cli_ctl/instance.py @@ -78,7 +78,7 @@ def instance_create(inst, log, args): def instance_example(inst, log, args): header = """ -; +; ; This is a version 2 ds setup inf file. ; It is used by the python versions of setup-ds-* ; Most options map 1 to 1 to the original .inf file.