alnoda-workspaces/workspaces/base-workspace/Cronicle-0.8.61/htdocs/js/pages/admin/Servers.js
2021-07-30 12:18:29 +00:00

391 lines
15 KiB
JavaScript
Executable file

// Cronicle Admin Page -- Servers
Class.add( Page.Admin, {
gosub_servers: function(args) {
// show server list, server groups
this.div.removeClass('loading');
app.setWindowTitle( "Servers" );
var size = get_inner_window_size();
var col_width = Math.floor( ((size.width * 0.9) + 400) / 9 );
var html = '';
html += this.getSidebarTabs( 'servers',
[
['activity', "Activity Log"],
['api_keys', "API Keys"],
['categories', "Categories"],
['plugins', "Plugins"],
['servers', "Servers"],
['users', "Users"]
]
);
html += '<div style="padding:20px 20px 30px 20px">';
// Active Server Cluster
var cols = ['Hostname', 'IP Address', 'Groups', 'Status', 'Active Jobs', 'Uptime', 'CPU', 'Mem', 'Actions'];
html += '<div class="subtitle">';
html += 'Server Cluster';
// html += '<div class="clear"></div>';
html += '</div>';
this.servers = [];
var hostnames = hash_keys_to_array( app.servers ).sort();
for (var idx = 0, len = hostnames.length; idx < len; idx++) {
this.servers.push( app.servers[ hostnames[idx] ] );
}
// include nearby servers under main server list
if (app.nearby) {
var hostnames = hash_keys_to_array( app.nearby ).sort();
for (var idx = 0, len = hostnames.length; idx < len; idx++) {
var server = app.nearby[ hostnames[idx] ];
if (!app.servers[server.hostname]) {
server.nearby = 1;
this.servers.push( server );
}
}
}
// render table
var self = this;
html += this.getBasicTable( this.servers, cols, 'server', function(server, idx) {
// render nearby servers differently
if (server.nearby) {
var tds = [
'<div class="td_big" style="font-weight:normal"><div class="ellip" style="max-width:'+col_width+'px;"><i class="fa fa-eye">&nbsp;</i>' + server.hostname.replace(/\.[\w\-]+\.\w+$/, '') + '</div></div>',
(server.ip || 'n/a').replace(/^\:\:ffff\:(\d+\.\d+\.\d+\.\d+)$/, '$1'),
'-', '(Nearby)', '-', '-', '-', '-',
'<span class="link" onMouseUp="$P().add_server_from_list('+idx+')"><b>Add Server</b></span>'
];
tds.className = 'blue';
return tds;
} // nearby
var actions = [
'<span class="link" onMouseUp="$P().restart_server('+idx+')"><b>Restart</b></span>',
'<span class="link" onMouseUp="$P().shutdown_server('+idx+')"><b>Shutdown</b></span>'
];
if (server.disabled) actions = [];
if (!server.master) {
actions.push( '<span class="link" onMouseUp="$P().remove_server('+idx+')"><b>Remove</b></span>' );
}
var group_names = [];
var eligible = false;
for (var idx = 0, len = app.server_groups.length; idx < len; idx++) {
var group = app.server_groups[idx];
var regexp = new RegExp( group.regexp, "i" );
if (server.hostname.match(regexp)) {
group_names.push( group.title );
if (group.master) eligible = true;
}
}
var jobs = find_objects( app.activeJobs, { hostname: server.hostname } );
var num_jobs = jobs.length;
var cpu = 0;
var mem = 0;
if (server.data && server.data.cpu) cpu += server.data.cpu;
if (server.data && server.data.mem) mem += server.data.mem;
for (idx = 0, len = jobs.length; idx < len; idx++) {
var job = jobs[idx];
if (job.cpu && job.cpu.current) cpu += job.cpu.current;
if (job.mem && job.mem.current) mem += job.mem.current;
}
var tds = [
'<div class="td_big">' + self.getNiceGroup(null, server.hostname, col_width) + '</div>',
(server.ip || 'n/a').replace(/^\:\:ffff\:(\d+\.\d+\.\d+\.\d+)$/, '$1'),
group_names.length ? group_names.join(', ') : '(None)',
server.master ? '<span class="color_label green"><i class="fa fa-check">&nbsp;</i>Primary</span>' : (eligible ? '<span class="color_label purple">Backup</span>' : '<span class="color_label blue">Worker</span>'),
num_jobs ? commify( num_jobs ) : '(None)',
get_text_from_seconds( server.uptime, true, true ).replace(/\bday\b/, 'days'),
short_float(cpu) + '%',
get_text_from_bytes(mem),
actions.join(' | ')
];
if (server.disabled) tds.className = 'disabled';
return tds;
} );
html += '<div style="height:25px;"></div>';
html += '<center><table><tr>';
html += '<td><div class="button" style="width:130px;" onMouseUp="$P().add_server()"><i class="fa fa-plus-circle">&nbsp;&nbsp;</i>Add Server...</div></td>';
html += '</tr></table></center>';
html += '<div style="height:30px;"></div>';
// Server Groups
var col_width = Math.floor( ((size.width * 0.9) + 300) / 6 );
var cols = ['Title', 'Hostname Match', '# of Servers', '# of Events', 'Class', 'Actions'];
html += '<div class="subtitle">';
html += 'Server Groups';
// html += '<div class="clear"></div>';
html += '</div>';
// sort by title ascending
this.server_groups = app.server_groups.sort( function(a, b) {
// return (b.title < a.title) ? 1 : -1;
return a.title.toLowerCase().localeCompare( b.title.toLowerCase() );
} );
// render table
var self = this;
html += this.getBasicTable( this.server_groups, cols, 'group', function(group, idx) {
var actions = [
'<span class="link" onMouseUp="$P().edit_group('+idx+')"><b>Edit</b></span>',
'<span class="link" onMouseUp="$P().delete_group('+idx+')"><b>Delete</b></span>'
];
var regexp = new RegExp( group.regexp, "i" );
var num_servers = 0;
for (var hostname in app.servers) {
if (hostname.match(regexp)) num_servers++;
}
var group_events = find_objects( app.schedule, { target: group.id } );
var num_events = group_events.length;
return [
'<div class="td_big" style="white-space:nowrap;"><span class="link" onMouseUp="$P().edit_group('+idx+')">' + self.getNiceGroup(group, null, col_width) + '</span></div>',
'<div class="ellip" style="font-family:monospace; max-width:'+col_width+'px;">/' + encode_entities(group.regexp) + '/</div>',
// group.description || '(No description)',
num_servers ? commify( num_servers) : '(None)',
num_events ? commify( num_events ) : '(None)',
group.master ? '<b>Primary Eligible</b>' : 'Worker Only',
actions.join(' | ')
];
} );
html += '<div style="height:25px;"></div>';
html += '<center><table><tr>';
html += '<td><div class="button" style="width:130px;" onMouseUp="$P().edit_group(-1)"><i class="fa fa-plus-circle">&nbsp;&nbsp;</i>Add Group...</div></td>';
html += '</tr></table></center>';
html += '</div>'; // padding
html += '</div>'; // sidebar tabs
this.div.html( html );
},
add_server_from_list: function(idx) {
// add a server right away, from the nearby list
var server = this.servers[idx];
app.showProgress( 1.0, "Adding server..." );
app.api.post( 'app/add_server', { hostname: server.ip || server.hostname }, function(resp) {
app.hideProgress();
app.showMessage('success', "Server was added successfully.");
// self['gosub_servers'](self.args);
} ); // api.post
},
add_server: function() {
// show dialog allowing user to enter an arbitrary hostname to add
var html = '';
// html += '<div style="font-size:12px; color:#777; margin-bottom:15px;">Typically, servers should automatically add themselves to the cluster, if they are within UDP broadcast range (i.e. on the same LAN). You should only need to manually add a server in special circumstances, e.g. if it is remotely hosted in another datacenter or network.</div>';
// html += '<div style="font-size:12px; color:#777; margin-bottom:20px;">Note that the new server cannot already be a master server, nor part of another '+app.name+' server cluster, and the current master server must be able to reach it.</div>';
html += '<center><table>' +
// get_form_table_spacer() +
get_form_table_row('Hostname or IP:', '<input type="text" id="fe_as_hostname" style="width:280px" value="" spellcheck="false"/>') +
get_form_table_caption("Enter the hostname or IP of the server you want to add.") +
'</table></center>';
app.confirm( '<i class="mdi mdi-desktop-tower mdi-lg">&nbsp;&nbsp;</i>Add Server', html, "Add Server", function(result) {
app.clearError();
if (result) {
var hostname = $('#fe_as_hostname').val().toLowerCase();
if (!hostname) return app.badField('fe_as_hostname', "Please enter a server hostname or IP address.");
if (!hostname.match(/^[\w\-\.]+$/)) return app.badField('fe_as_hostname', "Please enter a valid server hostname or IP address.");
if (app.servers[hostname]) return app.badField('fe_as_hostname', "That server is already in the cluster.");
Dialog.hide();
app.showProgress( 1.0, "Adding server..." );
app.api.post( 'app/add_server', { hostname: hostname }, function(resp) {
app.hideProgress();
app.showMessage('success', "Server was added successfully.");
// self['gosub_servers'](self.args);
} ); // api.post
} // user clicked add
} ); // app.confirm
setTimeout( function() {
$('#fe_as_hostname').focus();
}, 1 );
},
remove_server: function(idx) {
// remove manual server after user confirmation
var server = this.servers[idx];
var jobs = find_objects( app.activeJobs, { hostname: server.hostname } );
if (jobs.length) return app.doError("Sorry, you cannot remove a server that has active jobs running on it.");
// proceed with remove
var self = this;
app.confirm( '<span style="color:red">Remove Server</span>', "Are you sure you want to remove the server <b>"+server.hostname+"</b>?", "Remove", function(result) {
if (result) {
app.showProgress( 1.0, "Removing server..." );
app.api.post( 'app/remove_server', server, function(resp) {
app.hideProgress();
app.showMessage('success', "Server was removed successfully.");
// self.gosub_servers(self.args);
} );
}
} );
},
edit_group: function(idx) {
// edit group (-1 == new group)
var self = this;
var group = (idx > -1) ? this.server_groups[idx] : {
title: "",
regexp: "",
master: 0
};
var edit = (idx > -1) ? true : false;
var html = '';
html += '<table>';
// Internal ID
if (edit && this.isAdmin()) {
html += get_form_table_row( 'Group ID', '<div style="font-size:14px;">' + group.id + '</div>' );
html += get_form_table_caption( "The internal Group ID used for API calls. This cannot be changed." );
html += get_form_table_spacer();
}
html +=
get_form_table_row('Group Title:', '<input type="text" id="fe_eg_title" size="25" value="'+escape_text_field_value(group.title)+'"/>') +
get_form_table_caption("Enter a title for the server group, short and sweet.") +
get_form_table_spacer() +
get_form_table_row('Hostname Match:', '<input type="text" id="fe_eg_regexp" size="30" style="font-family:monospace; font-size:13px;" value="'+escape_text_field_value(group.regexp)+'" spellcheck="false"/>') +
get_form_table_caption("Enter a regular expression to auto-assign servers to this group by their hostnames, e.g. \"^mtx\\d+\\.\".") +
get_form_table_spacer() +
get_form_table_row('Server Class:', '<select id="fe_eg_master">' + render_menu_options([ [1,'Primary Eligible'], [0,'Worker Only'] ], group.master, false) + '</select>') +
get_form_table_caption("Select whether servers in the group are eligible to become the primary server, or run as workers only.") +
'</table>';
app.confirm( '<i class="mdi mdi-server-network">&nbsp;&nbsp;</i>' + (edit ? "Edit Server Group" : "Add Server Group"), html, edit ? "Save Changes" : "Add Group", function(result) {
app.clearError();
if (result) {
group.title = $('#fe_eg_title').val();
if (!group.title) return app.badField('fe_eg_title', "Please enter a title for the server group.");
group.regexp = $('#fe_eg_regexp').val().replace(/^\/(.+)\/$/, '$1');
if (!group.regexp) return app.badField('fe_eg_regexp', "Please enter a regular expression for the server group.");
try { new RegExp(group.regexp); }
catch(err) {
return app.badField('fe_eg_regexp', "Invalid regular expression: " + err);
}
group.master = parseInt( $('#fe_eg_master').val() );
Dialog.hide();
// pro-tip: embed id in title as bracketed prefix
if (!edit && group.title.match(/^\[(\w+)\]\s*(.+)$/)) {
group.id = RegExp.$1;
group.title = RegExp.$2;
}
app.showProgress( 1.0, edit ? "Saving group..." : "Adding group..." );
app.api.post( edit ? 'app/update_server_group' : 'app/create_server_group', group, function(resp) {
app.hideProgress();
app.showMessage('success', "Server group was " + (edit ? "saved" : "added") + " successfully.");
// self['gosub_servers'](self.args);
} ); // api.post
} // user clicked add
} ); // app.confirm
setTimeout( function() {
if (!$('#fe_eg_title').val()) $('#fe_eg_title').focus();
}, 1 );
},
delete_group: function(idx) {
// delete selected server group
var group = this.server_groups[idx];
// make sure user isn't deleting final master group
if (group.master) {
var num_masters = 0;
for (var idx = 0, len = this.server_groups.length; idx < len; idx++) {
if (this.server_groups[idx].master) num_masters++;
}
if (num_masters == 1) {
return app.doError("Sorry, you cannot delete the last Primary Eligible server group.");
}
}
// check for events first
var group_events = find_objects( app.schedule, { target: group.id } );
var num_events = group_events.length;
if (num_events) return app.doError("Sorry, you cannot delete a group that has events assigned to it.");
// proceed with delete
var self = this;
app.confirm( '<span style="color:red">Delete Server Group</span>', "Are you sure you want to delete the server group <b>"+group.title+"</b>? There is no way to undo this action.", "Delete", function(result) {
if (result) {
app.showProgress( 1.0, "Deleting group..." );
app.api.post( 'app/delete_server_group', group, function(resp) {
app.hideProgress();
app.showMessage('success', "Server group was deleted successfully.");
// self.gosub_servers(self.args);
} );
}
} );
},
restart_server: function(idx) {
// restart server after confirmation
var self = this;
var server = this.servers[idx];
app.confirm( '<span style="color:red">Restart Server</span>', "Are you sure you want to restart the server <b>"+server.hostname+"</b>? All server jobs will be aborted.", "Restart", function(result) {
if (result) {
app.showProgress( 1.0, "Restarting server..." );
app.api.post( 'app/restart_server', server, function(resp) {
app.hideProgress();
app.showMessage('success', "Server is being restarted in the background.");
// self.gosub_servers(self.args);
} );
}
} );
},
shutdown_server: function(idx) {
// shutdown server after confirmation
var self = this;
var server = this.servers[idx];
app.confirm( '<span style="color:red">Shutdown Server</span>', "Are you sure you want to shutdown the server <b>"+server.hostname+"</b>? All server jobs will be aborted.", "Shutdown", function(result) {
if (result) {
app.showProgress( 1.0, "Shutting down server..." );
app.api.post( 'app/shutdown_server', server, function(resp) {
app.hideProgress();
app.showMessage('success', "Server is being shut down in the background.");
// self.gosub_servers(self.args);
} );
}
} );
}
});