Commit 7a2cfab1 authored by Tristan Cavelier's avatar Tristan Cavelier

monitor2: add monitor password interface

parent dc578acb
......@@ -106,7 +106,7 @@ recipe = slapos.recipe.template:jinja2
filename = template-monitor.cfg
template = ${:_profile_base_location_}/instance-monitor.cfg.jinja2.in
rendered = ${buildout:directory}/template-monitor.cfg
md5sum = f0356d4a0048dc22de4f61191f7caa7e
md5sum = c3438660ae33d0049fceaf45ddb781bf
context =
key apache_location apache:location
key gzip_location gzip:location
......@@ -114,6 +114,7 @@ context =
raw monitor_conf_template ${monitor-conf:location}/${monitor-conf:filename}
raw monitor_password_promise_template ${monitor-password-promise:location}/${monitor-password-promise:filename}
raw monitor_password_cgi_template ${monitor-password-cgi:location}/${monitor-password-cgi:filename}
raw monitor_password_promise_interface_template ${monitor-password-promise-interface:location}/${monitor-password-promise-interface:filename}
raw monitor_web_default_promise_interface ${monitor-web-default-promise-interface:location}/${monitor-web-default-promise-interface:filename}
raw monitor_web_index_html ${monitor-web-index-html:location}/${monitor-web-index-html:filename}
raw monitor_web_monitor_css ${monitor-web-monitor-css:location}/${monitor-web-monitor-css:filename}
......@@ -213,6 +214,11 @@ md5sum = 0a9a42551ed6bdb973fd1f0dd1d4ec86
md5sum = 04fc7e6d892d29a601cfd43d1700eeda
filename = monitor-password.py.cgi
[monitor-password-promise-interface]
<= monitor-download-base
filename = monitor-password-interface.html
md5sum = 04b664dfb47bfd3d01502768311aa239
[rss-bin]
recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/${:filename}
......
......@@ -66,6 +66,9 @@ www = ${directory:monitor}/web
web-dir = ${directory:monitor}/web
log = ${directory:log}/monitor
promise-wrapper = ${directory:var}/monitor-promise-wrapper
monitor-var = ${directory:var}/monitor
monitor-password-var = ${monitor-directory:monitor-var}/password
monitor-password-interface = ${monitor-directory:monitor-password-var}/password/interface
[logrotate-directory]
recipe = slapos.cookbook:mkdirectory
......@@ -287,6 +290,7 @@ context =
[monitor-password-promise-conf-parameter]
title = Monitor password
frequency = */5 * * * *
private-path-list = ${monitor-directory:monitor-password-interface}
[monitor-password-promise-conf]
recipe = slapos.recipe.template:jinja2
......@@ -305,6 +309,12 @@ context =
raw htpasswd_executable {{ apache_location }}/bin/htpasswd
key htpasswd_path httpd-monitor-htpasswd:htpasswd-path
[monitor-password-promise-interface]
recipe = slapos.recipe.template:jinja2
template = {{ monitor_password_promise_interface_template }}
rendered = ${monitor-directory:monitor-password-interface}/index.html
context =
[publish]
recipe = slapos.cookbook:publish
monitor-url = ${monitor-httpd-conf-parameter:url}
......@@ -331,4 +341,5 @@ parts =
monitor-password-promise
monitor-password-promise-conf
monitor-password-cgi
monitor-password-promise-interface
publish
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Monitor password</title>
<style>
input, button {
min-height: 10mm;
min-width: 10mm;
}
</style>
<script>
var service_name = "monitor-password", // XXX hardcoded
monitor_json_url = "/monitor.haljson",
status_json_url = "/public/" + service_name + ".status.json",
rerun_cgi_url = "/cgi-bin/monitor-run-promise.cgi?service=" + service_name,
password_cgi_url_part = "/cgi-bin/monitor-password.cgi?password=";
function newDeferred() {
var d = {
"promise": undefined,
"resolve": undefined,
"reject": undefined
};
d.promise = new Promise(function (resolve, reject) {
d.resolve = resolve;
d.reject = reject;
});
return d;
}
function xhr(param) {
/*global XMLHttpRequest */
var d = newDeferred(), xhr = new XMLHttpRequest(), k, i, l, a;
d.promise.cancel = function () { xhr.abort(); };
if (param.username) {
xhr.open((param.method || "GET").toUpperCase(), param.url, true, param.username, param.password);
} else {
xhr.open((param.method || "GET").toUpperCase(), param.url, true);
}
xhr.responseType = param.responseType || "";
if (param.withCredentials !== undefined) {
xhr.withCredentials = param.withCredentials;
}
if (param.headers) {
a = Object.keys(param.headers);
l = a.length;
for (i = 0; i < l; i += 1) {
k = a[i];
xhr.setRequestHeader(k, param.headers[k]);
}
}
xhr.addEventListener("load", function (e) {
var r, t = e.target, callback;
if (param.noStatusCheck) {
d.resolve(t);
} else if (t.status < 400) {
d.resolve(t);
} else {
r = new Error("HTTP: " + (t.status ? t.status + " " : "") + (t.statusText || "Unknown"));
r.target = t;
d.reject(r);
}
}, false);
xhr.addEventListener("error", function (e) {
return d.reject(new Error("HTTP: Error"));
}, false);
xhr.addEventListener("abort", function (e) {
return d.reject(new Error("HTTP: Aborted"));
}, false);
xhr.send(param.data);
return d.promise;
}
function unexpectedError(reason) {
console.error(reason);
alert(reason);
}
function MonitorPasswordInterface(config) {
var it = this,
statusP = document.createElement("p"),
descriptionP = document.createElement("p"),
form = document.createElement("form"),
formPassword1Input = document.createElement("input"),
formPassword2Input = document.createElement("input"),
formChangePasswordButton = document.createElement("button"),
errorH2 = document.createElement("h2"),
errorPre = document.createElement("pre"),
header = document.createElement("header"),
h1 = document.createElement("h1"),
h2 = document.createElement("h2"),
a = document.createElement("a"),
button = document.createElement("button");
this.element = config.rootElement || document.createElement("div");
this.statusP = statusP;
this.descriptionP = descriptionP;
this.formPassword1Input = formPassword1Input;
this.formPassword2Input = formPassword2Input;
this.formChangePasswordButton = formChangePasswordButton;
this.errorH2 = errorH2;
this.errorPre = errorPre;
this.element.appendChild(header);
header.appendChild(a);
a.setAttribute("tabindex", "-1");
a.setAttribute("href", "/");
a.appendChild(button);
button.textContent = "Home";
a = document.createElement("a");
button = document.createElement("button");
header.appendChild(a);
a.setAttribute("tabindex", "-1");
a.setAttribute("href", "");
a.appendChild(button);
button.textContent = "Refresh";
this.element.appendChild(h1);
h1.textContent = "Monitor password";
this.element.appendChild(statusP);
this.element.appendChild(descriptionP);
this.element.appendChild(form);
form.appendChild(formPassword1Input);
formPassword1Input.setAttribute("type", "password");
form.onsubmit = this.onFormSubmit.bind(this);
form.appendChild(document.createElement("br"));
form.appendChild(formPassword2Input);
formPassword2Input.setAttribute("type", "password");
form.appendChild(document.createElement("br"));
form.appendChild(formChangePasswordButton);
formChangePasswordButton.setAttribute("type", "submit");
formChangePasswordButton.textContent = "Change password";
this.element.appendChild(errorH2);
errorH2.textContent = "Operational error";
errorH2.style.display = "none";
this.element.appendChild(errorPre);
errorPre.style.display = "none";
this.loadStatusUi();
this.loadDescriptionUi();
this.loadErrorUi();
}
MonitorPasswordInterface.prototype.loadStatusJson = function () {
if (this.status_json_promise) { return; }
this.status_json_promise = Promise.resolve().then(function () {
return xhr({url: status_json_url, withCredentials: true, responseType: "json"});
}).then(function (xhr) {
return xhr.response;
});
this.status_json_promise.catch(function () { return; }).then(function () {
setTimeout(function () {
delete this.status_json_promise;
}.bind(this), 1000);
}.bind(this));
return this.status_json_promise;
};
MonitorPasswordInterface.prototype.loadStatusUi = function () {
this.loadStatusJson();
this.statusP.textContent = "Loading status...";
return this.status_json_promise.then(function (status_json) {
if (status_json.status === "OK") {
this.statusP.innerHTML = "&nbsp;";
} else {
this.statusP.textContent = "/!\\ The password needs to be changed at least once! /!\\";
}
}.bind(this), function (reason) {
if (reason && reason.target && reason.target.status === 404) {
this.statusP.textContent = "/!\\ The password needs to be changed at least once! /!\\";
return;
}
var message = reason && (reason.target && (reason.target.statusText || "Unknown") || reason.message);
this.statusP.textContent = "Status Json Error: " + (message || "Unknown error");
}.bind(this)).catch(unexpectedError);
};
MonitorPasswordInterface.prototype.loadDescriptionUi = function () {
this.descriptionP.textContent = [
"The monitor password is the password used to connect to this interface.",
"Here you can change the monitor password by filling the formular just below."
].join("\n");
};
MonitorPasswordInterface.prototype.loadErrorUi = function () {
this.loadStatusJson();
this.errorPre.textContent = "Loading error output...";
return this.status_json_promise.then(function (status_json) {
if (status_json.error) {
this.errorH2.style.display = "";
this.errorPre.style.display = "";
this.errorPre.textContent = status_json.error;
} else {
this.errorH2.style.display = "none";
this.errorPre.style.display = "none";
this.errorPre.textContent = "";
}
}.bind(this), function (reason) {
var message = reason && (reason.target && (reason.target.statusText || "Unknown") || reason.message);
this.errorPre.textContent = "Status Json Error: " + (message || "Unknown error");
}.bind(this)).catch(unexpectedError);
};
MonitorPasswordInterface.prototype.onFormSubmit = function (event) {
event.preventDefault();
event.stopPropagation();
this.execForm();
};
MonitorPasswordInterface.prototype.execForm = function () {
if (this.formPassword1Input.value !== this.formPassword2Input.value) {
this.statusP.textContent = "The two typed passwords should match!";
return;
}
this.statusP.textContent = "Changing password...";
var password = this.formPassword1Input.value;
return Promise.resolve().then(function () {
return xhr({url: password_cgi_url_part + password, method: "POST", withCredentials: true});
}).then(function () {
this.statusP.textContent = "Password changed succesfully!";
this.formPassword1Input.value = this.formPassword2Input.value = "";
// rerun promise with new login (also does the relogin)
xhr({url: rerun_cgi_url, method: "POST", withCredentials: true, username: "admin", password: password});
}.bind(this), function (reason) {
var message = reason && (reason.target && (reason.target.statusText || "Unknown") || reason.message);
this.statusP.textContent = "Status Json Error: " + (message || "Unknown error");
}.bind(this));
};
MonitorPasswordInterface.prototype.runPromiseNow = function () {
this.runPromiseNowButton.disabled = true;
var original_text = this.runPromiseNowButton.textContent;
this.runPromiseNowButton.textContent = "Sending message...";
return Promise.resolve().then(function () {
return xhr({url: rerun_cgi_url, method: "POST", withCredentials: true});
}).catch(unexpectedError).then(function () {
this.runPromiseNowButton.textContent = original_text;
}.bind(this));
};
/*global setTimeout */
setTimeout(function () {
/*global document */
document.body.innerHTML = "";
return new MonitorPasswordInterface({rootElement: document.body});
});
</script>
</head>
<body>
<h1>Monitor password</h1>
<noscript>Javascript should be enabled</noscript>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment