Commit 2405ccff authored by Roque's avatar Roque

[with mock data] extend monitoring app storage

- use slapos master as remote sub storage
- handle all data maniputation within the storage
- sync cleans objects of non-present slapos masters
- add slapos_master_url in all opml tree objects
- new storage layer to handle storage url and limit
- separate replicate and webhttp storage files
- extend test coverage
parent d5ae73d6
/*
* Copyright 2016, Nexedi SA
* Released under the LGPL license.
* http://www.gnu.org/licenses/lgpl.html
*/
/*jslint nomen: true*/
/*global jIO, RSVP */
(function (jIO, RSVP) {
"use strict";
var LIMIT = 100; //default
/**
* Monitor erp5 layer to wrap erp5 storages for monitor app
*
* @class ERP55Monitor
* @constructor
*/
function ERP55Monitor(spec) {
if (!spec.sub_storage || spec.sub_storage.type !== 'erp5') {
throw new TypeError("ERP55Monitor subtorage must be erp5 type");
}
if (spec.limit) {
LIMIT = spec.limit;
}
this._storage_definition = spec.sub_storage;
this._sub_storage = jIO.createJIO(spec.sub_storage);
}
ERP55Monitor.prototype.get = function (id) {
return this._sub_storage.get.apply(this._sub_storage, arguments);
};
ERP55Monitor.prototype.post = function () {
return this._sub_storage.post.apply(this._sub_storage, arguments);
};
ERP55Monitor.prototype.put = function () {
return this._sub_storage.put.apply(this._sub_storage, arguments);
};
ERP55Monitor.prototype.remove = function () {
return this._sub_storage.remove.apply(this._sub_storage, arguments);
};
ERP55Monitor.prototype.hasCapacity = function (capacity) {
return (capacity === "list") || (capacity === "limit") || (capacity === "include") || (capacity === "query") || (capacity === "select");
};
ERP55Monitor.prototype.buildQuery = function () {
var sub_storage = this._sub_storage, args = arguments, master_url = this._storage_definition.url, i;
if (!arguments[0].limit) {
arguments[0].limit = [0, LIMIT];
}
return new RSVP.Queue()
.push(function () {
return sub_storage.buildQuery.apply(sub_storage, args);
})
.push(function (result) {
for (i = 0; i < result.length; i += 1) {
result[i].master_url = master_url;
}
return result;
});
};
jIO.addStorage('erp5monitor', ERP55Monitor);
}(jIO, RSVP));
\ No newline at end of file
/*global console, btoa, Blob*/
/*jslint nomen: true, maxlen: 200*/
(function (window, QUnit, jIO, rJS) {
"use strict";
var test = QUnit.test,
equal = QUnit.equal,
expect = QUnit.expect,
ok = QUnit.ok,
stop = QUnit.stop,
start = QUnit.start,
deepEqual = QUnit.deepEqual,
slapos_master_url_list = ["https://panel.rapid.space/hateoas/", "https://softinst239021.host.vifib.net/erp5/web_site_module/slapos_hateoas/"];
rJS(window)
.ready(function (g) {
///////////////////////////
// Monitoring storage
///////////////////////////
return g.run({
type: "replicatedopml",
remote_storage_unreachable_status: "WARNING",
remote_opml_check_time_interval: 86400000,
request_timeout: 25000, // timeout is to 25 second
local_sub_storage: {
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "indexeddb",
database: "monitoring_local_roque.db"
}
}
},
remote_sub_storage: {
type: "union",
storage_list: [
{
type: "erp5monitor",
limit: 20,
sub_storage: {
type: "erp5",
url: slapos_master_url_list[0],
default_view_reference: "jio_view"
}
},
{
type: "erp5monitor",
limit: 20,
sub_storage: {
type: "erp5",
url: slapos_master_url_list[1],
default_view_reference: "jio_view"
}
}
]
}
});
})
.declareMethod('run', function (jio_options) {
test('Test "' + jio_options.type + '"scenario', function () {
var jio, jio_definition = jio_options,
first_master_total_docs, opml_foo_url = "https://foo-opml.bar";
stop();
expect(25);
//Ensure no previous test db is present
return new RSVP.Queue()
.push(function () {
return indexedDB.deleteDatabase("jio:" + DB_NAME);
})
.then(function () {
try {
jio = jIO.createJIO(jio_options);
} catch (error) {
console.error(error.stack);
console.error(error);
throw error;
}
// Try to fetch inexistent document
jio.get("inexistent")
})
.fail(function (error) {
if (error.status_code !== 404) {
throw error;
}
equal(error.status_code, 404, "404 if inexistent");
})
.then(function () {
//first sync
console.log("Sync for master 1 and 2");
return jio.repair();
})
.fail(function (error) {
console.error("---");
console.error(error.stack);
console.error(error);
ok(false, error);
})
//check if repair minimally worked
.then(function () {
return jio.allDocs();
})
.then(function (all_docs) {
ok(all_docs.data.total_rows > 0, 'Repair succeded. (if not, please be sure to be logged in masters)');
})
//TODO test allAttachments
//call methods that are not implemented (fail expected)
.then(function () {
return jio.getAttachment("foo", "bar");
})
.fail(function (error) {
if (error.status_code !== 501) {
throw error;
}
equal(error.status_code, 501, "400 if no getAttachment method");
})
.then(function () {
return jio.putAttachment("foo",
"bar",
new Blob(["fooo"], {type: "text/plain"}));
})
.fail(function (error) {
if (error.status_code !== 501) {
throw error;
}
equal(error.status_code, 501, "501 if no putAttachment method");
})
.then(function () {
return jio.post({});
})
.fail(function (error) {
if (error.status_code !== 501) {
throw error;
}
equal(error.status_code, 501, "501 if no post method");
})
//check limit and sort are implemented
.then(function () {
return jio.allDocs({limit: [0, 3], sort_on: [["creation_date", "descending"]]});
})
//check specific type of objects were created after repair
.then(function (all_docs) {
ok(all_docs.data.total_rows === 3, 'Limit capacity implemented.');
return RSVP.all([
jio.allDocs({query: 'portal_type: "Instance Tree"'}),
jio.allDocs({query: 'portal_type: "Software Instance"'}),
jio.allDocs({query: 'portal_type: "Promise"'}),
jio.allDocs({query: 'portal_type: "Opml"'}),
jio.allDocs({query: 'portal_type: "Opml Outline"'}),
jio.allDocs({include_docs: true}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[0] + '"'}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[1] + '"'})
]);
})
.then(function (all_doc_list) {
var id_list = [], get_list = [], all_docs = all_doc_list[5].data.rows, i, push_queue = new RSVP.Queue();
first_master_total_docs = all_doc_list[6].data.total_rows;
console.log("Objects created for master 1:", first_master_total_docs);
console.log("Objects created for master 2:", all_doc_list[7].data.total_rows);
ok(first_master_total_docs > 0, 'Objects created for master 1.');
ok(all_doc_list[7].data.total_rows > 0, 'Objects created for master 2.');
ok(all_doc_list[0].data.total_rows > 0, 'Instance Tree object created after sync.');
id_list.push(all_doc_list[0].data.rows[0].id); //save one id to check later in all docs
ok(all_doc_list[1].data.total_rows > 0, 'Software Instance object created after sync.');
id_list.push(all_doc_list[1].data.rows[0].id);
ok(all_doc_list[2].data.total_rows > 0, 'Promise object created after sync.');
id_list.push(all_doc_list[2].data.rows[0].id);
ok(all_doc_list[3].data.total_rows > 0, 'Opml object created after sync.');
id_list.push(all_doc_list[3].data.rows[0].id);
ok(all_doc_list[4].data.total_rows > 0, 'Opml Outline object created after sync.');
id_list.push(all_doc_list[4].data.rows[0].id);
for (i = 0; i < id_list.length; i += 1) {
var idd = id_list[i];
get_list.push(jio.get(idd));
}
for (i = 0; i < all_docs.length; i += 1) {
//check different elements are present in plain allDocs
if (id_list.includes(all_docs[i].id)) {
const index = id_list.indexOf(all_docs[i].id);
id_list.splice(index, 1);
}
}
ok(id_list.length === 0, 'Different types created objects are returned by allDocs');
console.log("Total amount of docs after first repair:", all_docs.length);
return RSVP.all(get_list);
})
.then(function (get_list_result) {
//check specific objects
equal(get_list_result[0].portal_type, "Instance Tree", "Check instance tree object.");
equal(get_list_result[1].portal_type, "Software Instance", "Check software instance object.");
equal(get_list_result[2].portal_type, "Promise", "Check promise tree object.");
equal(get_list_result[3].portal_type, "Opml", "Check opml object.");
equal(get_list_result[4].portal_type, "Opml Outline", "Check opml outline object.");
//update jio storage slapos master urls (drop one)
jio_definition.remote_sub_storage.storage_list = [
{
type: "erp5monitor",
limit: 20,
sub_storage: {
type: "erp5",
url: slapos_master_url_list[0],
default_view_reference: "jio_view"
}
}
];
try {
jio = jIO.createJIO(jio_options);
} catch (error) {
console.error(error.stack);
console.error(error);
throw error;
}
console.log("Sync for only master 1 (master 2 removed)");
return jio.repair();
})
.then(function () {
//check objects for each slapos master
return RSVP.all([
jio.allDocs({include_docs: true}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[0] + '"'}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[1] + '"'})
]);
})
.then(function (all_results) {
console.log("Total amount of docs after second repair:", all_results[0].data.total_rows);
console.log("Objects from master 1:", all_results[1].data.total_rows);
console.log("Objects from master 2:", all_results[2].data.total_rows);
ok(all_results[1].data.total_rows === first_master_total_docs, "Objects of kept master url must be kept");
ok(all_results[2].data.total_rows === 0, "Removed master url objects must be removed");
})
.then(function () {
//manually add an opml
var opml_dict = {
type: "Opml",
title: "foo opml",
portal_type: "Opml",
url: opml_foo_url,
basic_login: "basic_login",
username: "username",
password: "password",
active: true,
has_monitor: true,
state: "Started",
slapos_master_url: "foo-master",
manually_added: true
};
return jio.put(opml_foo_url, opml_dict);
})
.then(function () {
console.log("Sync for ompl manually added (master 1 kept)");
return jio.repair();
})
.then(function () {
return RSVP.all([
jio.allDocs({include_docs: true}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[0] + '"'}),
jio.get(opml_foo_url),
jio.allDocs({query: 'slapos_master_url: ""'})
]);
})
.then(function (all_results) {
console.log("Total amount of docs after third repair:", all_results[0].data.total_rows);
console.log("From manual opml (no master):", all_results[3].data.total_rows);
equal(all_results[2].url, opml_foo_url, "Opml was added.");
//check new objects were created, besides ompl itself and previous master ones
ok(all_results[0].data.total_rows >= first_master_total_docs + 1, "New objects created for added opml");
})
//TODO change masters and sync, and then check ompl and its objects are kept?
.then(function () {
//remove opml
return jio.remove(opml_foo_url);
})
.then(function () {
console.log("Sync for removed ompl (master 1 kept)");
return jio.repair();
})
.then(function () {
return RSVP.all([
jio.allDocs({include_docs: true}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[0] + '"'})
]);
})
.then(function (all_results) {
//check opml objects were removed too
console.log("Total amount of docs after fourth repair:", all_results[0].data.total_rows);
ok(all_results[0].data.total_rows === first_master_total_docs && all_results[1].data.total_rows === first_master_total_docs, "Objects of kept master url must be kept");
return jio.get(opml_foo_url);
})
.fail(function (error) {
if (error.status_code !== 404) {
throw error;
}
equal(error.status_code, 404, "Opml was removed.");
})
.then(function () {
return RSVP.all([
jio.allDocs({include_docs: true}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[0] + '"'}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[1] + '"'}),
jio.allDocs({query: 'slapos_master_url: ""'})
]);
})
.then(function (all_results) {
console.log("Finished. Total amount of docs:", all_results[0].data.total_rows);
console.log("From master 1:", all_results[1].data.total_rows);
console.log("From master 2:", all_results[2].data.total_rows);
console.log("No master (manual opml)):", all_results[3].data.total_rows);
console.log("Full doc list:", all_results);
})
.always(function () {
start();
});
});
});
}(window, QUnit, jIO, rJS));
\ No newline at end of file
<!DOCTYPE html>
<!--
Copyright 2014, Nexedi SA
This program is free software: you can Use, Study, Modify and Redistribute
it under the terms of the GNU General Public License version 3, or (at your
option) any later version, as published by the Free Software Foundation.
You can also Link and Combine this program with other software covered by
the terms of any of the Free Software licenses or any of the Open Source
Initiative approved licenses and Convey the resulting work. Corresponding
source of such a combination shall include the source code for all other
software used.
This program is distributed WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See COPYING file for full licensing terms.
See https://www.nexedi.com/licensing for rationale and options.
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>jIO Coverage for Monitoring union storage</title>
<script src="../external/rsvp-2.0.4.js"></script>
<script src="../external/renderjs-latest.js"></script>
<script src="../dist/jio-latest.js"></script>
<link rel="stylesheet" href="../external/qunit.css" type="text/css" media="screen"/>
<script src="../external/qunit.js" type="text/javascript"></script>
<script src="monitoring-union.js"></script>
<script src="replicateopml.js"></script>
<script src="webhttpstorage.js"></script>
<script src="erp5monitorapp.js"></script>
</head>
<body>
<h1 id="qunit-header">jIO Coverage Monitoring union storage</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup, will be hidden</div>
</body>
</html>
/*global console, btoa, Blob*/
/*jslint nomen: true, maxlen: 200*/
(function (window, QUnit, jIO, rJS) {
"use strict";
var test = QUnit.test,
equal = QUnit.equal,
expect = QUnit.expect,
ok = QUnit.ok,
stop = QUnit.stop,
start = QUnit.start,
deepEqual = QUnit.deepEqual,
slapos_master_url_list = ["https://panel.rapid.space/hateoas/", "https://softinst239021.host.vifib.net/erp5/web_site_module/slapos_hateoas/"];
rJS(window)
.ready(function (g) {
///////////////////////////
// Monitoring storage
///////////////////////////
return g.run({
type: "replicatedopml",
remote_storage_unreachable_status: "WARNING",
remote_opml_check_time_interval: 86400000,
request_timeout: 25000, // timeout is to 25 second
local_sub_storage: {
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "indexeddb",
database: "monitoring_local_roque.db"
}
}
},
remote_sub_storage: {
type: "union",
storage_list: [
{
type: "erp5monitor",
limit: 20,
sub_storage: {
type: "erp5",
url: slapos_master_url_list[0],
default_view_reference: "jio_view"
}
},
{
type: "erp5monitor",
limit: 20,
sub_storage: {
type: "erp5",
url: slapos_master_url_list[1],
default_view_reference: "jio_view"
}
}
]
}
});
})
.declareMethod('run', function (jio_options) {
test('Test "' + jio_options.type + '"scenario', function () {
var jio, jio_definition = jio_options,
first_master_total_docs, opml_foo_url = "https://foo-opml.bar";
stop();
expect(25);
try {
jio = jIO.createJIO(jio_options);
} catch (error) {
console.error(error.stack);
console.error(error);
throw error;
}
// Try to fetch inexistent document
jio.get("inexistent")
.fail(function (error) {
if (error.status_code !== 404) {
throw error;
}
equal(error.status_code, 404, "404 if inexistent");
})
.then(function () {
//first sync
console.log("Sync for master 1 and 2");
return jio.repair();
})
.fail(function (error) {
console.error("---");
console.error(error.stack);
console.error(error);
ok(false, error);
})
//check if repair minimally worked
.then(function () {
return jio.allDocs();
})
.then(function (all_docs) {
ok(all_docs.data.total_rows > 0, 'Repair succeded. (if not, please be sure to be logged in masters)');
})
//TODO test allAttachments
//call methods that are not implemented (fail expected)
.then(function () {
return jio.getAttachment("foo", "bar");
})
.fail(function (error) {
if (error.status_code !== 501) {
throw error;
}
equal(error.status_code, 501, "400 if no getAttachment method");
})
.then(function () {
return jio.putAttachment("foo",
"bar",
new Blob(["fooo"], {type: "text/plain"}));
})
.fail(function (error) {
if (error.status_code !== 501) {
throw error;
}
equal(error.status_code, 501, "501 if no putAttachment method");
})
.then(function () {
return jio.post({});
})
.fail(function (error) {
if (error.status_code !== 501) {
throw error;
}
equal(error.status_code, 501, "501 if no post method");
})
//check limit and sort are implemented
.then(function () {
return jio.allDocs({limit: [0, 3], sort_on: [["creation_date", "descending"]]});
})
//check specific type of objects were created after repair
.then(function (all_docs) {
ok(all_docs.data.total_rows === 3, 'Limit capacity implemented.');
return RSVP.all([
jio.allDocs({query: 'portal_type: "Instance Tree"'}),
jio.allDocs({query: 'portal_type: "Software Instance"'}),
jio.allDocs({query: 'portal_type: "Promise"'}),
jio.allDocs({query: 'portal_type: "Opml"'}),
jio.allDocs({query: 'portal_type: "Opml Outline"'}),
jio.allDocs({include_docs: true}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[0] + '"'}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[1] + '"'})
]);
})
.then(function (all_doc_list) {
var id_list = [], get_list = [], all_docs = all_doc_list[5].data.rows, i, push_queue = new RSVP.Queue();
first_master_total_docs = all_doc_list[6].data.total_rows;
console.log("Objects created for master 1:", first_master_total_docs);
console.log("Objects created for master 2:", all_doc_list[7].data.total_rows);
ok(first_master_total_docs > 0, 'Objects created for master 1.');
ok(all_doc_list[7].data.total_rows > 0, 'Objects created for master 2.');
ok(all_doc_list[0].data.total_rows > 0, 'Instance Tree object created after sync.');
id_list.push(all_doc_list[0].data.rows[0].id); //save one id to check later in all docs
ok(all_doc_list[1].data.total_rows > 0, 'Software Instance object created after sync.');
id_list.push(all_doc_list[1].data.rows[0].id);
ok(all_doc_list[2].data.total_rows > 0, 'Promise object created after sync.');
id_list.push(all_doc_list[2].data.rows[0].id);
ok(all_doc_list[3].data.total_rows > 0, 'Opml object created after sync.');
id_list.push(all_doc_list[3].data.rows[0].id);
ok(all_doc_list[4].data.total_rows > 0, 'Opml Outline object created after sync.');
id_list.push(all_doc_list[4].data.rows[0].id);
for (i = 0; i < id_list.length; i += 1) {
var idd = id_list[i];
get_list.push(jio.get(idd));
}
for (i = 0; i < all_docs.length; i += 1) {
//check different elements are present in plain allDocs
if (id_list.includes(all_docs[i].id)) {
const index = id_list.indexOf(all_docs[i].id);
id_list.splice(index, 1);
}
}
ok(id_list.length === 0, 'Different types created objects are returned by allDocs');
console.log("Total amount of docs after first repair:", all_docs.length);
return RSVP.all(get_list);
})
.then(function (get_list_result) {
//check specific objects
equal(get_list_result[0].portal_type, "Instance Tree", "Check instance tree object.");
equal(get_list_result[1].portal_type, "Software Instance", "Check software instance object.");
equal(get_list_result[2].portal_type, "Promise", "Check promise tree object.");
equal(get_list_result[3].portal_type, "Opml", "Check opml object.");
equal(get_list_result[4].portal_type, "Opml Outline", "Check opml outline object.");
//update jio storage slapos master urls (drop one)
jio_definition.remote_sub_storage.storage_list = [
{
type: "erp5monitor",
limit: 20,
sub_storage: {
type: "erp5",
url: slapos_master_url_list[0],
default_view_reference: "jio_view"
}
}
];
try {
jio = jIO.createJIO(jio_options);
} catch (error) {
console.error(error.stack);
console.error(error);
throw error;
}
console.log("Sync for only master 1 (master 2 removed)");
return jio.repair();
})
.then(function () {
//check objects for each slapos master
return RSVP.all([
jio.allDocs({include_docs: true}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[0] + '"'}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[1] + '"'})
]);
})
.then(function (all_results) {
console.log("Total amount of docs after second repair:", all_results[0].data.total_rows);
console.log("Objects from master 1:", all_results[1].data.total_rows);
console.log("Objects from master 2:", all_results[2].data.total_rows);
ok(all_results[1].data.total_rows === first_master_total_docs, "Objects of kept master url must be kept");
ok(all_results[2].data.total_rows === 0, "Removed master url objects must be removed");
})
.then(function () {
//manually add an opml
var opml_dict = {
type: "Opml",
title: "foo opml",
portal_type: "Opml",
url: opml_foo_url,
basic_login: "basic_login",
username: "username",
password: "password",
active: true,
has_monitor: true,
state: "Started",
slapos_master_url: "foo-master",
manually_added: true
};
return jio.put(opml_foo_url, opml_dict);
})
.then(function () {
console.log("Sync for ompl manually added (master 1 kept)");
return jio.repair();
})
.then(function () {
return RSVP.all([
jio.allDocs({include_docs: true}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[0] + '"'}),
jio.get(opml_foo_url),
jio.allDocs({query: 'slapos_master_url: ""'})
]);
})
.then(function (all_results) {
console.log("Total amount of docs after third repair:", all_results[0].data.total_rows);
console.log("From manual opml (no master):", all_results[3].data.total_rows);
equal(all_results[2].url, opml_foo_url, "Opml was added.");
//check new objects were created, besides ompl itself and previous master ones
ok(all_results[0].data.total_rows >= first_master_total_docs + 1, "New objects created for added opml");
})
//TODO change masters and sync, and then check ompl and its objects are kept?
.then(function () {
//remove opml
return jio.remove(opml_foo_url);
})
.then(function () {
console.log("Sync for removed ompl (master 1 kept)");
return jio.repair();
})
.then(function () {
return RSVP.all([
jio.allDocs({include_docs: true}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[0] + '"'})
]);
})
.then(function (all_results) {
//check opml objects were removed too
console.log("Total amount of docs after fourth repair:", all_results[0].data.total_rows);
ok(all_results[0].data.total_rows === first_master_total_docs && all_results[1].data.total_rows === first_master_total_docs, "Objects of kept master url must be kept");
return jio.get(opml_foo_url);
})
.fail(function (error) {
if (error.status_code !== 404) {
throw error;
}
equal(error.status_code, 404, "Opml was removed.");
})
.then(function () {
return RSVP.all([
jio.allDocs({include_docs: true}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[0] + '"'}),
jio.allDocs({query: 'slapos_master_url: "' + slapos_master_url_list[1] + '"'}),
jio.allDocs({query: 'slapos_master_url: ""'})
]);
})
.then(function (all_results) {
console.log("Finished. Total amount of docs:", all_results[0].data.total_rows);
console.log("From master 1:", all_results[1].data.total_rows);
console.log("From master 2:", all_results[2].data.total_rows);
console.log("No master (manual opml)):", all_results[3].data.total_rows);
console.log("Full doc list:", all_results);
})
.always(function () {
start();
});
});
});
}(window, QUnit, jIO, rJS));
\ No newline at end of file
/*jslint indent: 2 nomen: true */
/*global jIO, RSVP, DOMParser, Rusha, Blob, console, btoa, URLSearchParams */
(function (jIO, RSVP, DOMParser, Rusha, Blob, console, btoa, URLSearchParams) {
"use strict";
var rusha = new Rusha(),
mock_test = true,
MOCK_OPMLS = '[{"id":"/0","value":{},"doc":{"dateModified":"Thu, 04 Jul 2024 18:48:28 +0000","dateCreated":"Thu, 04 Jul 2024 09:00:47 +0000","title":"ERP5 Playground instance"}},{"id":"/1/0","value":{},"doc":{"text":"Monitoring RSS Feed list"}},{"id":"/1/0/3","value":{},"doc":{"url":"https://softinst220514.host.vifib.net/share/private/","xmlUrl":"https://softinst220514.host.vifib.net/public/feed","htmlUrl":"https://softinst220514.host.vifib.net/public/feed","version":"RSS","type":"rss","title":"PBS COMP-3557-theia-1","text":"PBS COMP-3557-theia-1","portal_type":"Opml Outline","parent_id":"f185fe2fa2280798a181e3fa45d2cad763d10540","parent_url":"https://softinst184163.host.vifib.net/public/feeds","reference":"2f9748e8ec9656b70ebf08dfec841509d506a8aa","active":true,"dateCreated":"Thu, 04 Jul 2024 09:00:47 +0000","dateModified":"Thu, 04 Jul 2024 18:48:28 +0000","opml_title":"ERP5 Playground instance"}},{"id":"/1/0/2","value":{},"doc":{"url":"https://softinst184178.host.vifib.net/share/private/","xmlUrl":"https://softinst184178.host.vifib.net/public/feed","htmlUrl":"https://softinst184178.host.vifib.net/public/feed","version":"RSS","type":"rss","title":"theia1","text":"theia1","portal_type":"Opml Outline","parent_id":"f185fe2fa2280798a181e3fa45d2cad763d10540","parent_url":"https://softinst184163.host.vifib.net/public/feeds","reference":"e5a8721558fba656175533e09b5023b24ad1aeed","active":true,"dateCreated":"Thu, 04 Jul 2024 09:00:47 +0000","dateModified":"Thu, 04 Jul 2024 18:48:28 +0000","opml_title":"ERP5 Playground instance"}},{"id":"/1/0/1","value":{},"doc":{"url":"https://softinst184170.host.vifib.net/share/private/","xmlUrl":"https://softinst184170.host.vifib.net/public/feed","htmlUrl":"https://softinst184170.host.vifib.net/public/feed","version":"RSS","type":"rss","title":"theia0","text":"theia0","portal_type":"Opml Outline","parent_id":"f185fe2fa2280798a181e3fa45d2cad763d10540","parent_url":"https://softinst184163.host.vifib.net/public/feeds","reference":"62bcecf32735232bf9b4fde41698bbe743dcd438","active":true,"dateCreated":"Thu, 04 Jul 2024 09:00:47 +0000","dateModified":"Thu, 04 Jul 2024 18:48:28 +0000","opml_title":"ERP5 Playground instance"}},{"id":"/1/0/0","value":{},"doc":{"url":"https://softinst184163.host.vifib.net/share/private/","xmlUrl":"https://softinst184163.host.vifib.net/public/feed","htmlUrl":"https://softinst184163.host.vifib.net/public/feed","version":"RSS","type":"rss","title":"ERP5 Playground instance","text":"ERP5 Playground instance","portal_type":"Opml Outline","parent_id":"f185fe2fa2280798a181e3fa45d2cad763d10540","parent_url":"https://softinst184163.host.vifib.net/public/feeds","reference":"305ddc8bed6419578add7a4cb19a0960a9b8a89d","active":true,"dateCreated":"Thu, 04 Jul 2024 09:00:47 +0000","dateModified":"Thu, 04 Jul 2024 18:48:28 +0000","opml_title":"ERP5 Playground instance"}}]',
MOCK_PROMISE = '{"data":{"rows":[{"id":"/0","value":{},"doc":{"docs":"http://blogs.law.harvard.edu/tech/rss","generator":"PyRSS2Gen-1.1.0","lastBuildDate":"Thu, 04 Jul 2024 23:04:14 GMT","description":"ERP5 Playground instance","link":"https://softinst220514.host.vifib.net/public/feed","title":"PBS COMP-3557-theia-1"}},{"id":"/0/0","value":{},"doc":{"source":"check-free-disk-space","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:12:11+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBjaGVjay1mcmVlLWRpc2stc3BhY2UsIDAyYTA1MWVmMjYwYWM3Y2RiNDE5ZmMwOTljNDRlM2U3LCAyMDI0LTA1LTEyVDA1OjEyOjExKzAwMDA=","guid_isPermaLink":"false","category":"OK","description":"Current disk usage: OKEnable to display disk space predictions: False","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] check-free-disk-space"}},{"id":"/0/1","value":{},"doc":{"source":"ssh-to-COMP-3557-slappart27-theia-1-pull","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-07-04T20:42:10+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBzc2gtdG8tQ09NUC0zNTU3LXNsYXBwYXJ0MjctdGhlaWEtMS1wdWxsLCBkNDFkOGNkOThmMDBiMjA0ZTk4MDA5OThlY2Y4NDI3ZSwgMjAyNC0wNy0wNFQyMDo0MjoxMCswMDAw","guid_isPermaLink":"false","category":"OK","description":"","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] ssh-to-COMP-3557-slappart27-theia-1-pull"}},{"id":"/0/2","value":{},"doc":{"source":"buildout-slappart46-status","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:46+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBidWlsZG91dC1zbGFwcGFydDQ2LXN0YXR1cywgODY1MzBjNTE5MmIwMzA1MjlhODJiZTY1NDZkMWI5MmYsIDIwMjQtMDUtMTJUMDU6MzA6NDYrMDAwMA==","guid_isPermaLink":"false","category":"OK","description":"buildout is OK","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] buildout-slappart46-status"}},{"id":"/0/3","value":{},"doc":{"source":"notifier-feed-check-malformed-or-failure","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:45+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBub3RpZmllci1mZWVkLWNoZWNrLW1hbGZvcm1lZC1vci1mYWlsdXJlLCA1NTZiNThmNzE3ZjY5Yjk0M2Q1NTc1YTZjNGU3M2U2YiwgMjAyNC0wNS0xMlQwNTozMDo0NSswMDAw","guid_isPermaLink":"false","category":"OK","description":"OK /srv/slapgrid/slappart46/bin/notifier-feed-check-malformed-or-failure.py run with success","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] notifier-feed-check-malformed-or-failure"}},{"id":"/0/4","value":{},"doc":{"source":"monitor-httpd-listening-on-tcp","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:46+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBtb25pdG9yLWh0dHBkLWxpc3RlbmluZy1vbi10Y3AsIDhkYmE4OTAyMTcxNzRhNzdhMzM5NWVkNjI0MWE0Y2Y5LCAyMDI0LTA1LTEyVDA1OjMwOjQ2KzAwMDA=","guid_isPermaLink":"false","category":"OK","description":"non-authenticated request to https://[2001:67c:1254:103::5975]:8070 succeeded (returned expected code 401)","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] monitor-httpd-listening-on-tcp"}},{"id":"/0/5","value":{},"doc":{"source":"ssh-to-COMP-3557-slappart27-theia-1-push","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-07-04T22:54:25+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBzc2gtdG8tQ09NUC0zNTU3LXNsYXBwYXJ0MjctdGhlaWEtMS1wdXNoLCBkNDFkOGNkOThmMDBiMjA0ZTk4MDA5OThlY2Y4NDI3ZSwgMjAyNC0wNy0wNFQyMjo1NDoyNSswMDAw","guid_isPermaLink":"false","category":"OK","description":"","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] ssh-to-COMP-3557-slappart27-theia-1-push"}},{"id":"/0/6","value":{},"doc":{"source":"check-monitor-frontend-password","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:46+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBjaGVjay1tb25pdG9yLWZyb250ZW5kLXBhc3N3b3JkLCA1YWRjMTAzMDhiYWVhYWJmMWZhZDIyMDBjOTQ0ODc2OSwgMjAyNC0wNS0xMlQwNTozMDo0NiswMDAw","guid_isPermaLink":"false","category":"OK","description":"authenticated request to https://softinst220514.host.vifib.net succeeded (returned expected code 200)","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] check-monitor-frontend-password"}},{"id":"/0/7","value":{},"doc":{"source":"stalled-pull-push","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:46+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBzdGFsbGVkLXB1bGwtcHVzaCwgZWI3Mjc5NWJlM2IzYTQwNjg1Nzg3YTk0NWFmMTcyZjEsIDIwMjQtMDUtMTJUMDU6MzA6NDYrMDAwMA==","guid_isPermaLink":"false","category":"OK","description":"OK /srv/slapgrid/slappart46/bin/stalled-pull-push run with success","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] stalled-pull-push"}},{"id":"/0/8","value":{},"doc":{"source":"monitor-bootstrap-status","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:46+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBtb25pdG9yLWJvb3RzdHJhcC1zdGF0dXMsIGY2MjBiYTUxMjc4Y2U3YjIxZjlhNzg1ZDc4OThkZThjLCAyMDI0LTA1LTEyVDA1OjMwOjQ2KzAwMDA=","guid_isPermaLink":"false","category":"OK","description":"Bootstrap OK","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] monitor-bootstrap-status"}},{"id":"/0/9","value":{},"doc":{"source":"monitor-http-frontend","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:45+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBtb25pdG9yLWh0dHAtZnJvbnRlbmQsIDNiMTcwYjc0Njk0MTNjMTUxZTkxMmU5MDI4ZjFjNWVkLCAyMDI0LTA1LTEyVDA1OjMwOjQ1KzAwMDA=","guid_isPermaLink":"false","category":"OK","description":"non-authenticated request to https://softinst220514.host.vifib.net succeeded (returned expected code 401)","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] monitor-http-frontend"}}],"total_rows":11}}',
MOCK_INSTANCE = '{"data":{"rows":[{"id":"monitor.global","value":{},"doc":{"status":"OK","state":{"error":0,"success":21},"type":"global","portal_type":"Software Instance","date":"2024-07-04T23:10:20+0000","_links":{"rss_url":{"href":"https://softinst184178.host.vifib.net/public/feed"},"public_url":{"href":"https://softinst184178.host.vifib.net/share/public/"},"private_url":{"href":"https://softinst184178.host.vifib.net/share/private/"},"related_monitor":[]},"data":{"state":"monitor_state.data","process_state":"monitor_process_resource.status","process_resource":"monitor_resource_process.data","memory_resource":"monitor_resource_memory.data","io_resource":"monitor_resource_io.data","monitor_process_state":"monitor_resource.status"},"title":"theia1","specialise_title":"ERP5 Playground instance","aggregate_reference":"COMP-3490","ipv4":"10.0.225.11","ipv6":"2001:67c:1254:f1::f6f0","software_release":"https://lab.nexedi.com/nexedi/slapos/raw/1.0.358/software/theia/software.cfg","software_type":"import","partition_id":"slappart30","parameters":[{"key":"","title":"monitor-user","value":"admin"},{"key":"monitor-password","title":"monitor-password","value":"CQo1zXbIB0XqAwYt"},{"key":"min-free-disk-MB","title":"min-free-disk-MB","value":""}]}}],"total_rows":1}}',
OPML_ATTACHMENT_NAME = "__opml__",
PROMISE_TYPE = "Promise",
SOFTWARE_INSTANCE_TYPE = "Software Instance",
INSTANCE_TREE_TYPE = "Instance Tree",
OPML_PORTAL_TYPE = "Opml",
ZONE_LIST = [
"-1200",
"-1100",
"-1000",
"-0900",
"-0800",
"-0700",
"-0600",
"-0500",
"-0400",
"-0300",
"-0200",
"-0100",
"+0000",
"+0100",
"+0200",
"+0300",
"+0400",
"+0500",
"+0600",
"+0700",
"+0800",
"+0900",
"+1000",
"+1100",
"+1200"
];
function generateHash(str) {
return rusha.digestFromString(str);
}
function createStorage(context, storage_spec, key) {
var signature;
signature = generateHash(JSON.stringify(storage_spec));
if (!context._remote_storage_dict.hasOwnProperty(key) ||
signature !== context._remote_storage_dict[key].signature) {
context._remote_storage_dict[key] = {
storage: jIO.createJIO(storage_spec),
signature: signature
};
}
return context._remote_storage_dict[key].storage;
}
/**
* The JIO OPML Tree Replicated Storage extension for monitor
*
* @class ReplicatedOPMLStorage
* @constructor
*/
function ReplicatedOPMLStorage(spec) {
if (typeof spec.type !== 'string') {
throw new TypeError(
"ReplicatedOPMLStorage 'type' is not a string"
);
}
if (spec.local_sub_storage === undefined) {
throw new TypeError("ReplicatedOPMLStorage 'local_sub_storage' " +
"is not defined");
}
this._local_sub_storage = jIO.createJIO(spec.local_sub_storage);
this._local_sub_storage_spec = spec.local_sub_storage;
if (spec.remote_sub_storage !== undefined) {
this._remote_sub_storage_spec = spec.remote_sub_storage;
this._remote_sub_storage = jIO.createJIO(spec.remote_sub_storage);
}
this._remote_storage_unreachable_status =
spec.remote_storage_unreachable_status;
this._remote_storage_dict = {};
this._remote_parser_storage_type = spec.remote_parser_storage_type;
if (this._remote_parser_storage_type === undefined) {
this._remote_parser_storage_type = "parser";
}
this._remote_opml_check_time_interval =
spec.remote_opml_check_time_interval;
if (this._remote_opml_check_time_interval === undefined) {
// one day in miliseconds
this._remote_opml_check_time_interval = 86400000;
}
this._request_timeout = spec.request_timeout;
if (this._request_timeout === undefined) {
this._request_timeout = 0; // no timeout
}
}
ReplicatedOPMLStorage.prototype.get = function () {
return this._local_sub_storage.get.apply(this._local_sub_storage,
arguments);
};
ReplicatedOPMLStorage.prototype.buildQuery = function () {
return this._local_sub_storage.buildQuery.apply(this._local_sub_storage,
arguments);
};
ReplicatedOPMLStorage.prototype.put = function (id, doc) {
//allow app configuration types (forms, views, actions, etc)
/*if (!doc.hasOwnProperty('portal_type') || doc.portal_type !== 'opml') {
throw new TypeError("Cannot put object which portal_type is not 'opml'");
}*/
if (doc.active === undefined) {
doc.active = true;
}
return this._local_sub_storage.put(id, doc);
};
ReplicatedOPMLStorage.prototype.hasCapacity = function (capacity) {
var this_storage_not_capacity_list = ['post', 'getAttachment', 'putAttachment'];
if (this_storage_not_capacity_list.indexOf(capacity) !== -1) {
return false;
}
if (capacity === 'include') {
return true;
}
return this._local_sub_storage.hasCapacity.apply(this._local_sub_storage,
arguments);
};
ReplicatedOPMLStorage.prototype.allAttachments = function () {
return this._local_sub_storage.allAttachments.apply(this._local_sub_storage,
arguments);
};
ReplicatedOPMLStorage.prototype.remove = function (id) {
//the removal of an opml involves to remove the full opml tree asociated
//note: if the opml belongs to a slapos master, next sync will restore it
var storage = this._local_sub_storage;
return storage.get(id)
.push(function (doc) {
if (doc.portal_type !== OPML_PORTAL_TYPE) {
return storage.remove(id);
}
function removeOPMLTree(url) {
var remove_id_list = [],
remove_signature_id_list = [];
// remove related instance tree
remove_id_list.push(generateHash(id));
// removed saved opml content
remove_signature_id_list.push({
id: url,
name: url
});
// remove all related documents
return storage.allDocs({
select_list: ["xmlUrl", "url"],
query: '(portal_type:"Opml Outline") AND (parent_url:"' + url + '")'
})
.push(function (document_result) {
var i,
query_list = [];
for (i = 0; i < document_result.data.total_rows; i += 1) {
query_list.push('(parent_id:"' +
document_result.data.rows[i].id + '")');
remove_id_list.push(document_result.data.rows[i].id);
remove_signature_id_list.push({
id: document_result.data.rows[i].id,
name: document_result.data.rows[i].value.xmlUrl
});
remove_signature_id_list.push({
id: document_result.data.rows[i].id,
name: document_result.data.rows[i].value.url
});
}
// cleanup all sub opml items
if (query_list.length > 0) {
return storage.allDocs({query: query_list.join(" OR ")});
}
return {data: {total_rows: 0}};
})
.push(function (sub_item_result) {
var j,
i,
k,
remove_queue = new RSVP.Queue();
function removeItem(key) {
remove_queue
.push(function () {
return storage.remove(key);
});
}
function removeAttachmentItem(id, name) {
remove_queue
.push(function () {
return storage.removeAttachment(id, name);
})
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return undefined;
}
throw error;
});
}
// remove signatures
for (k = 0; k < remove_signature_id_list.length; k += 1) {
removeAttachmentItem(
remove_signature_id_list[k].id,
remove_signature_id_list[k].name
);
}
// remove opml-outline sub-items (rss)
for (j = 0; j < sub_item_result.data.total_rows; j += 1) {
removeItem(sub_item_result.data.rows[j].id);
}
// remove opml-outline
for (i = 0; i < remove_id_list.length; i += 1) {
removeItem(remove_id_list[i]);
}
return remove_queue;
})
.push(function () {
return storage.remove(url);
});
}
return removeOPMLTree(id);
});
};
function getStorageUrl(storage_spec) {
var spec = storage_spec;
while (spec !== undefined) {
if (spec.url !== undefined) {
return spec.url;
}
if (spec.document_id !== undefined) {
return spec.document_id;
}
spec = spec.sub_storage;
}
throw new Error("No url found on sub storage: " +
JSON.stringify(storage_spec));
}
function getDocumentAsAttachment(context, attachment_id, name) {
return context._local_sub_storage.getAttachment(attachment_id, name)
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return undefined;
}
console.error(error);
})
.push(function (attachment) {
if (attachment) {
return jIO.util.readBlobAsText(attachment)
.then(function (evt) {
return JSON.parse(evt.target.result);
});
}
return {};
});
}
function fixGlobalInstanceDocument(instance) {
// Fix some property as backed is old, to keep backward compatibility
// XXX - this method should be removed when all backend will be upgraded
if (instance._embedded !== undefined) {
if (instance._embedded.instance !== undefined) {
// set aggregate_reference to the computer reference and make it
// searchable
instance.aggregate_reference = instance._embedded.instance.computer;
}
if (instance._embedded.hasOwnProperty('promises')) {
// remove useless information from the document
delete instance._embedded.promises;
}
}
if (instance.hasOwnProperty('hosting-title')) {
// hosting-title should be specialise_title
instance.specialise_title = instance['hosting-title'];
delete instance['hosting-title'];
}
// set portal_type is not defined
if (!instance.hasOwnProperty('portal_type')) {
instance.portal_type = SOFTWARE_INSTANCE_TYPE;
}
return instance;
}
function updateSubStorageStatus(context, signature_dict, next_status) {
var key,
update_status_queue = new RSVP.Queue();
function updateStatus(id) {
update_status_queue
.push(function () {
return context._local_sub_storage.get(id);
})
.push(function (doc) {
if (doc.portal_type === PROMISE_TYPE) {
doc.category = next_status;
return context._local_sub_storage.put(id, doc);
}
if (doc.status !== undefined) {
doc.status = next_status;
return context._local_sub_storage.put(id, doc);
}
});
}
for (key in signature_dict) {
if (signature_dict.hasOwnProperty(key)) {
if (signature_dict[key].status !== next_status) {
updateStatus(key);
signature_dict[key].status = next_status;
}
}
}
return update_status_queue
.push(function () {
return signature_dict;
});
}
function loadSubStorage(context, storage_spec, parent_id, index, type) {
var sub_storage,
result_dict,
storage_key,
url;
url = getStorageUrl(storage_spec);
storage_key = generateHash(parent_id + url);
sub_storage = createStorage(context, storage_spec, storage_key);
result_dict = {
parent_id: parent_id,
type: type || storage_spec.type,
current_signature: {},
result: {data: {total_rows: 0}},
url: url,
parent_index: index
};
return sub_storage.allDocs({include_docs: true})
.push(undefined, function (error) {
//throw error;
console.error(error);
if (mock_test) {
if (index === 2) {
return JSON.parse(MOCK_PROMISE);
}
if (index === 3) {
return JSON.parse(MOCK_INSTANCE);
}
}
return undefined;
})
.push(function (result) {
if (result === undefined) {
if (context._remote_storage_unreachable_status !== undefined) {
// update status of local documents
// and set unreachable status
return getDocumentAsAttachment(context, parent_id, url)
.push(function (signature_document) {
return updateSubStorageStatus(
context,
signature_document,
context._remote_storage_unreachable_status
);
})
.push(function (signature_dict) {
return signature_dict;
});
}
return {};
}
result_dict.result = result;
return getDocumentAsAttachment(
context,
parent_id,
url
);
})
.push(function (signature_document) {
result_dict.current_signature = signature_document;
return result_dict;
});
}
function updateInstanceTreeState(hosting, element) {
var status = element.status.toUpperCase();
if (hosting.instance_amount === 0) {
hosting.status_date = fixDateTimezone(element.date);
}
if (hosting.status === "ERROR") {
return;
} else if (status === "ERROR") {
hosting.status = status;
} else if (status === "WARNING") {
hosting.status = status;
}
if (status === "OK" && hosting.status !== status) {
hosting.status = status;
}
}
function fixDateTimezone(date_string) {
// set default timezone offset to UTC
// XXX should be removed later
/*if (ZONE_LIST.indexOf(date_string.slice(-5)) === -1) {
return date_string + "+0000";
}*/
return date_string;
}
function getOpmlTree(context, opml_url, opml_spec, basic_login, opml_title, slapos_master_url) {
var opml_storage,
opml_document_list = [],
delete_key_list = [],
attachment_document_list = [],
opml_result_list,
current_signature_dict = {},
fetch_remote_opml = false,
instance_tree,
id;
id = generateHash(opml_url);
opml_storage = createStorage(context, opml_spec, id);
// Instance Tree is build from OPML and it has status
instance_tree = {
title: opml_title || "",
portal_type: INSTANCE_TREE_TYPE,
opml_url: opml_url,
status: "WARNING",
instance_amount: 0,
status_date: (new Date()).toUTCString() + "+0000",
slapos_master_url: slapos_master_url
};
return getDocumentAsAttachment(context, opml_url, OPML_ATTACHMENT_NAME)
.push(function (opml_doc) {
var current_time = new Date().getTime();
if (opml_doc.expire_time !== undefined) {
fetch_remote_opml = (opml_doc.expire_time - current_time) < 0;
} else {
fetch_remote_opml = true;
}
if (fetch_remote_opml) {
return opml_storage.allDocs({include_docs: true})
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return {data: {total_rows: 0}};
}
//throw error;
console.error(error);
return {data: {total_rows: 0}};
})
.push(function (opml_result) {
opml_result_list = opml_result;
if (opml_result.data.total_rows > 0) {
attachment_document_list.push({
id: opml_url,
name: OPML_ATTACHMENT_NAME,
doc: {
expire_time: new Date().getTime() +
context._remote_opml_check_time_interval,
data: opml_result
}
});
return getDocumentAsAttachment(
context,
id,
opml_url
);
}
return {};
})
.push(function (signature_dict) {
current_signature_dict = signature_dict;
});
}
opml_result_list = opml_doc.data;
})
.push(function () {
var i,
item,
signature,
doc_signature_dict = {},
skip_add = false,
id_hash,
result_list = [],
header_dict = {};
if (opml_result_list.data.total_rows > 0) {
if (opml_result_list.data.rows[0].doc.title) {
instance_tree.title = opml_result_list.data.rows[0]
.doc.title;
}
if (fetch_remote_opml) {
header_dict = {
dateCreated: opml_result_list.data.rows[0].doc.dateCreated,
dateModified: opml_result_list.data.rows[0].doc.dateModified,
opml_title: opml_result_list.data.rows[0].doc.title
};
}
}
var iter = opml_result_list.data.total_rows;
if (mock_test) {
opml_result_list.data.rows = JSON.parse(MOCK_OPMLS);
opml_result_list.data.total_rows = 6;
iter = 4;
}
for (i = 1; i < iter; i += 1) {
item = opml_result_list.data.rows[i];
if (item.doc.xmlUrl !== undefined) {
id_hash = generateHash(id + item.id);
result_list.push(loadSubStorage(
context,
{
type: context._remote_parser_storage_type,
document_id: item.doc.xmlUrl,
attachment_id: 'enclosure',
parser: 'rss',
sub_storage: {
type: "http",
timeout: context._request_timeout
}
},
id_hash,
i,
PROMISE_TYPE
));
// Load private docs
if (item.doc.url !== undefined) {
result_list.push(loadSubStorage(
context,
{
type: 'webhttp',
url: item.doc.url.replace('jio_private', 'private'),
basic_login: basic_login,
timeout: context._request_timeout
},
id_hash,
i
));
}
if (fetch_remote_opml) {
// Append this document signature to the list
signature = generateHash(JSON.stringify(item.doc));
doc_signature_dict[id_hash] = {
signature: signature
};
if (current_signature_dict.hasOwnProperty(id_hash)) {
if (current_signature_dict[id_hash].signature === signature) {
// remote document was not modified, delete and skip add
delete current_signature_dict[id_hash];
skip_add = true;
}
delete current_signature_dict[id_hash];
}
Object.assign(item.doc, {
portal_type: "Opml Outline",
parent_id: id,
parent_url: opml_url,
reference: id_hash,
active: true,
slapos_master_url: slapos_master_url
});
Object.assign(item.doc, header_dict);
if (!skip_add) {
opml_document_list.push({
id: id_hash,
doc: item.doc
});
}
}
}
}
if (fetch_remote_opml && Object.keys(doc_signature_dict).length > 0) {
attachment_document_list.push({
id: opml_url,
name: opml_url,
doc: doc_signature_dict
});
delete_key_list.push.apply(delete_key_list,
Object.keys(current_signature_dict));
}
return RSVP.all(result_list);
})
.push(function (result_list) {
var i,
j,
start,
extra_dict;
function applyItemToTree(item, item_result, extra_dict) {
var id_hash,
element = item.doc,
signature,
item_id = item.guid || item.id,
status = (element.status || element.category),
item_signature_dict = {};
if (element.type === 'global') {
updateInstanceTreeState(instance_tree, element);
instance_tree.instance_amount += 1;
if (element.aggregate_reference === undefined) {
// XXX - document need to be updated to keep compatibility
element = fixGlobalInstanceDocument(element);
}
// XXX - fixing date timezone
element.date = fixDateTimezone(element.date);
}
// XXX - fixing date timezone
if (element.pubDate !== undefined) {
element.pubDate = fixDateTimezone(element.pubDate);
}
id_hash = generateHash(item_result.parent_id +
item_result.url + item_id);
if (extra_dict !== undefined) {
Object.assign(element, extra_dict);
}
// Generating document signature
signature = generateHash(JSON.stringify(element));
item_signature_dict[id_hash] = {
signature: signature,
status: status
};
if (item_result.current_signature.hasOwnProperty(id_hash)) {
if (item_result.current_signature[id_hash].signature ===
signature) {
// the document was not modified return
delete item_result.current_signature[id_hash];
return;
}
// the document exists and has changed
delete item_result.current_signature[id_hash];
}
Object.assign(element, {
parent_id: item_result.parent_id,
portal_type: element.portal_type || element.type ||
item_result.type,
status: status,
reference: element.reference || id_hash,
active: true,
slapos_master_url: slapos_master_url
});
opml_document_list.push({
id: id_hash,
doc: element
});
attachment_document_list.push({
id: item_result.parent_id,
name: item_result.url,
doc: item_signature_dict
});
}
for (i = 0; i < result_list.length; i += 1) {
extra_dict = undefined;
start = 0;
if (result_list[i].result.data.total_rows > 0) {
if (result_list[i].type === PROMISE_TYPE) {
// the first element of rss is the header
extra_dict = {
lastBuildDate: fixDateTimezone(result_list[i].result.data
.rows[0].doc.lastBuildDate),
channel: result_list[i].result.data.rows[0].doc.description,
channel_item: result_list[i].result.data.rows[0].doc.title
};
start = 1;
}
for (j = start; j < result_list[i].result.data.total_rows; j += 1) {
applyItemToTree(
result_list[i].result.data.rows[j],
result_list[i],
extra_dict
);
}
delete_key_list.push.apply(
delete_key_list,
Object.keys(result_list[i].current_signature)
);
} else if (Object.keys(result_list[i].current_signature).length > 0) {
// if the remote data is empty and current_signature is not empty,
// push to storage in case the status was changed
// this help for speed optimisation
attachment_document_list.push({
id: result_list[i].parent_id,
name: result_list[i].url,
doc: result_list[i].current_signature
});
} else if (context._remote_storage_unreachable_status !== undefined) {
if (result_list[i].type === "webhttp") {
// In case it was impossible to get software Instance
// Add an empty Software Instance with unreachable status
applyItemToTree(
{
id: "monitor.global",
doc: {
portal_type: SOFTWARE_INSTANCE_TYPE,
status: context._remote_storage_unreachable_status,
title: opml_result_list.data.rows[result_list[i]
.parent_index].doc.title,
date: new Date().toUTCString() + "+0000",
specialise_title: opml_result_list.data.rows[result_list[i]
.parent_index].doc.opml_title
}
},
result_list[i],
undefined
);
}
}
}
opml_document_list.push({
id: id,
doc: instance_tree
});
return [opml_document_list, delete_key_list, attachment_document_list];
});
}
function pushDocumentToStorage(context, document_list, delete_key_list,
attachment_document_list) {
var document_queue = new RSVP.Queue(),
i;
function pushDocument(id, element) {
document_queue
.push(function () {
return context._local_sub_storage.put(id, element);
});
}
for (i = 0; i < document_list.length; i += 1) {
pushDocument(
document_list[i].id,
document_list[i].doc
);
}
return document_queue
.push(function () {
var k,
remove_queue = new RSVP.Queue();
// remove all document which were not updated
function removeDocument(key) {
remove_queue
.push(function () {
return context._local_sub_storage.remove(key);
})
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return {};
}
throw error;
});
}
for (k = 0; k < delete_key_list.length; k += 1) {
removeDocument(delete_key_list[k]);
}
return remove_queue;
})
.push(function () {
var j,
signature_queue = new RSVP.Queue();
function pushAttachment(id, name, element) {
signature_queue
.push(function () {
return context._local_sub_storage.putAttachment(
id,
name,
new Blob([JSON.stringify(element)], {type : 'application/json'})
);
})
.push(undefined, function (error) {
console.error(error);
});
}
for (j = 0; j < attachment_document_list.length; j += 1) {
pushAttachment(
attachment_document_list[j].id,
attachment_document_list[j].name,
attachment_document_list[j].doc
);
}
});
}
function syncOpmlStorage(context) {
return context._local_sub_storage.allDocs({
query: '(portal_type:"' + OPML_PORTAL_TYPE + '") AND (active:true) AND (url:"https://%")',
select_list: ["title", "url", "basic_login", "slapos_master_url"]
})
.push(function (storage_result) {
var i,
opml_queue = new RSVP.Queue();
function syncFullOpml(storage_spec) {
opml_queue
.push(function () {
return getOpmlTree(
context,
storage_spec.url,
{
type: context._remote_parser_storage_type,
document_id: storage_spec.url,
attachment_id: 'enclosure',
parser: 'opml',
sub_storage: {
type: "http",
timeout: context._request_timeout
}
},
storage_spec.basic_login,
storage_spec.title,
storage_spec.slapos_master_url
);
})
.push(function (result_list) {
return pushDocumentToStorage(
context,
result_list[0],
result_list[1],
result_list[2]
);
});
}
for (i = 0; i < storage_result.data.total_rows; i += 1) {
syncFullOpml(storage_result.data.rows[i].value);
}
return opml_queue;
});
}
ReplicatedOPMLStorage.prototype.repair = function () {
var context = this,
has_failed = false,
error_msg = "",
argument_list = arguments;
function getParameterDictFromUrl(uri_param) {
if (uri_param.has('url') && uri_param.has('password') &&
uri_param.has('username') && uri_param.get('url').startsWith('http')) {
return {
opml_url: uri_param.get('url').trim(),
username: uri_param.get('username').trim(),
password: uri_param.get('password').trim()
};
}
}
function getParameterFromconnectionDict(connection_dict) {
if (connection_dict["monitor-url"] &&
connection_dict["monitor-url"].startsWith('http') &&
connection_dict["monitor-user"] &&
connection_dict["monitor-password"]) {
return {
opml_url: connection_dict["monitor-url"].trim(),
username: connection_dict["monitor-user"].trim(),
password: connection_dict["monitor-password"].trim()
};
}
}
function readMonitoringParameter(parmeter_xml) {
var parser = new DOMParser(),
xml_doc = parser.parseFromString(parmeter_xml, "text/xml"),
parameter,
uri_param,
json_parameter,
parameter_dict,
monitor_dict = {};
json_parameter = xml_doc.getElementById("_");
if (json_parameter !== undefined && json_parameter !== null) {
parameter_dict = JSON.parse(json_parameter.textContent);
if (parameter_dict.hasOwnProperty("monitor-setup-url")) {
return getParameterDictFromUrl(
new URLSearchParams(parameter_dict["monitor-setup-url"])
);
}
return getParameterFromconnectionDict(parameter_dict);
}
parameter = xml_doc.getElementById("monitor-setup-url");
if (parameter !== undefined && parameter !== null) {
// monitor-setup-url exists
uri_param = new URLSearchParams(parameter.textContent);
return getParameterDictFromUrl(uri_param);
}
parameter = xml_doc.getElementById("monitor-url");
if (parameter !== undefined && parameter !== null) {
monitor_dict.url = parameter.textContent.trim();
parameter = xml_doc.getElementById("monitor-user");
if (parameter === undefined && parameter !== null) {
return;
}
monitor_dict.username = parameter.textContent.trim();
parameter = xml_doc.getElementById("monitor-password");
if (parameter === undefined && parameter !== null) {
return;
}
monitor_dict.password = parameter.textContent.trim();
return monitor_dict;
}
}
function getInstanceOPMLList(storage) {
if (!storage) {
return [];
}
var instance_tree_list = [],
opml_list = [],
uid_dict = {};
return storage.allDocs({
query: '(portal_type:"Instance Tree") AND (validation_state:"validated")',
select_list: ['title', 'default_successor_uid', 'uid', 'slap_state', 'id']
})
.push(function (result) {
console.log("instance tree query result:", result);
var i, slapos_id, slapos_master_url = "",
uid_search_list = [];
for (i = 0; i < result.data.total_rows; i += 1) {
if (result.data.rows[i].value.slap_state !== "destroy_requested") {
//TODO could slapos_id be used to desambiguate identic title
//instances trees between different storages?
slapos_id = result.data.rows[i].value.title;
if (result.data.rows[i].master_url) {
slapos_master_url = result.data.rows[i].master_url;
}
instance_tree_list.push({
title: result.data.rows[i].value.title,
relative_url: result.data.rows[i].id,
slapos_id: slapos_id,
slapos_master_url: slapos_master_url,
active: (result.data.rows[i].value.slap_state ===
"start_requested") ? true : false,
state: (result.data.rows[i].value.slap_state ===
"start_requested") ? "Started" : "Stopped"
});
uid_search_list.push(result.data.rows[i].value.uid);
if (result.data.rows[i].value.default_successor_uid) {
uid_dict[result.data.rows[i].value.default_successor_uid] = i;
}
}
}
return storage.allDocs({
query: '(portal_type:"Software Instance") AND ' +
'(successor_related_uid:("' + uid_search_list.join('","') + '"))',
select_list: ['uid', 'successor_related_uid', 'connection_xml']
});
})
.push(function (result) {
console.log("software instance query result:", result);
var i,
tmp_parameter,
tmp_uid,
slapos_master_url = "";
for (i = 0; i < result.data.total_rows; i += 1) {
tmp_uid = result.data.rows[i].value.uid;
if (uid_dict.hasOwnProperty(tmp_uid)) {
tmp_parameter = readMonitoringParameter(result.data.rows[i].value.connection_xml);
if (tmp_parameter === undefined) {
tmp_parameter = {username: "", password: "", opml_url: undefined};
}
if (instance_tree_list[uid_dict[tmp_uid]]) {
if (result.data.rows[i].master_url) {
slapos_master_url = result.data.rows[i].master_url;
}
opml_list.push({
portal_type: OPML_PORTAL_TYPE,
title: instance_tree_list[uid_dict[tmp_uid]]
.title,
relative_url: instance_tree_list[uid_dict[tmp_uid]]
.relative_url,
url: tmp_parameter.opml_url || String(tmp_uid) + " NO MONITOR",
has_monitor: tmp_parameter.opml_url !== undefined,
username: tmp_parameter.username,
password: tmp_parameter.password,
basic_login: btoa(tmp_parameter.username + ':' +
tmp_parameter.password),
active: tmp_parameter.opml_url !== undefined &&
instance_tree_list[uid_dict[tmp_uid]].active,
state: instance_tree_list[uid_dict[tmp_uid]].state,
slapos_master_url: slapos_master_url
});
}
}
}
return opml_list;
});
}
function cleanOpmlStorage(context) {
//TODO use slapos_master_url in the query instead of iterate later
return context._local_sub_storage.allDocs({
query: '(portal_type:"' + OPML_PORTAL_TYPE + '")',
select_list: ["title", "url", "basic_login", "slapos_master_url", "manually_added"]
})
.push(function (result) {
function removeAllOPML(remove_opml_list, jio) {
var remove_queue = new RSVP.Queue(), i;
function remove_opml(id) {
remove_queue
.push(function () {
return jio.remove(id);
});
}
for (i = 0; i < remove_opml_list.length; i += 1) {
remove_opml(remove_opml_list[i].id);
}
return remove_queue;
}
var slapos_master_url_list = [], spec_list = context._remote_sub_storage_spec.storage_list,
i, remove_opml_list = [], opml_list = result.data.rows;
if (spec_list) {
for (i = 0; i < spec_list.length; i += 1) {
if (spec_list[i].sub_storage && spec_list[i].sub_storage.url) {
slapos_master_url_list.push(spec_list[i].sub_storage.url);
}
}
}
if (slapos_master_url_list.length > 0) {
for (i = 0; i < opml_list.length; i += 1) {
if (!opml_list[i].value.manually_added) {
if (!slapos_master_url_list.includes(opml_list[i].value.slapos_master_url)) {
remove_opml_list.push(opml_list[i]);
}
}
}
}
return RSVP.all([
removeAllOPML(remove_opml_list, context)
]);
});
}
return new RSVP.Queue()
//repair sub storage layers (local and remote)
.push(function () {
return context._local_sub_storage.repair.apply(
context._local_sub_storage,
argument_list
);
})
.push(function () {
if (context._remote_sub_storage) {
return context._remote_sub_storage.repair.apply(
context._remote_sub_storage,
argument_list
);
}
})
.push(function () {
//delete all opmls trees of no longer present slapos masters
//(in case master url list was updated)
return cleanOpmlStorage(context);
})
.push(function () {
//get opml list from remote slapos master(s)
return getInstanceOPMLList(context._remote_sub_storage);
})
.push(undefined, function (error) {
has_failed = true;
if (error.target) {
if (error.target.status === 401) {
error_msg = ": unauthorized access to slapos master";
}
if (error.target.status === 404) {
error_msg = ": slapos master url not found";
}
if (error.target.status - 500 > 0) {
error_msg = ": server error on slapos master side - " + error.target.status;
}
if (error.target.responseURL) {
error_msg += ". URL: " + error.target.responseURL;
}
} else {
console.log(error);
}
throw "Failed to import remote configurations" + error_msg;
})
.push(function (opml_list) {
//HARDCODED second master
if (mock_test) {
if (context._remote_sub_storage.__storage._storage_list[1]) {
if (context._remote_sub_storage.__storage._storage_list[1].__storage._storage_definition.url === "https://softinst239021.host.vifib.net/erp5/web_site_module/slapos_hateoas/") {
opml_list[6].slapos_master_url = "https://softinst239021.host.vifib.net/erp5/web_site_module/slapos_hateoas/";
}
} else {
opml_list.pop();
}
}
//store opmls in local sub storage
var i, push_queue = new RSVP.Queue();
function pushOPML(opml_dict) {
push_queue
.push(function () {
return context._local_sub_storage.put(opml_dict.url, opml_dict);
});
}
for (i = 0; i < opml_list.length; i += 1) {
pushOPML(opml_list[i]);
}
return push_queue;
})
.push(function () {
if (!has_failed) {
//sync storage using updated opml list
return syncOpmlStorage(context);
}
});
};
jIO.addStorage('replicatedopml', ReplicatedOPMLStorage);
}(jIO, RSVP, DOMParser, Rusha, Blob, console, btoa, URLSearchParams));
\ No newline at end of file
/*jslint indent: 2 nomen: true */
/*global jIO, RSVP, DOMParser, Rusha, Blob, console, btoa, URLSearchParams */
(function (jIO, RSVP, DOMParser, Rusha, Blob, console, btoa, URLSearchParams) {
"use strict";
var rusha = new Rusha(),
mock_test = true,
MOCK_OPMLS = '[{"id":"/0","value":{},"doc":{"dateModified":"Thu, 04 Jul 2024 18:48:28 +0000","dateCreated":"Thu, 04 Jul 2024 09:00:47 +0000","title":"ERP5 Playground instance"}},{"id":"/1/0","value":{},"doc":{"text":"Monitoring RSS Feed list"}},{"id":"/1/0/3","value":{},"doc":{"url":"https://softinst220514.host.vifib.net/share/private/","xmlUrl":"https://softinst220514.host.vifib.net/public/feed","htmlUrl":"https://softinst220514.host.vifib.net/public/feed","version":"RSS","type":"rss","title":"PBS COMP-3557-theia-1","text":"PBS COMP-3557-theia-1","portal_type":"Opml Outline","parent_id":"f185fe2fa2280798a181e3fa45d2cad763d10540","parent_url":"https://softinst184163.host.vifib.net/public/feeds","reference":"2f9748e8ec9656b70ebf08dfec841509d506a8aa","active":true,"dateCreated":"Thu, 04 Jul 2024 09:00:47 +0000","dateModified":"Thu, 04 Jul 2024 18:48:28 +0000","opml_title":"ERP5 Playground instance"}},{"id":"/1/0/2","value":{},"doc":{"url":"https://softinst184178.host.vifib.net/share/private/","xmlUrl":"https://softinst184178.host.vifib.net/public/feed","htmlUrl":"https://softinst184178.host.vifib.net/public/feed","version":"RSS","type":"rss","title":"theia1","text":"theia1","portal_type":"Opml Outline","parent_id":"f185fe2fa2280798a181e3fa45d2cad763d10540","parent_url":"https://softinst184163.host.vifib.net/public/feeds","reference":"e5a8721558fba656175533e09b5023b24ad1aeed","active":true,"dateCreated":"Thu, 04 Jul 2024 09:00:47 +0000","dateModified":"Thu, 04 Jul 2024 18:48:28 +0000","opml_title":"ERP5 Playground instance"}},{"id":"/1/0/1","value":{},"doc":{"url":"https://softinst184170.host.vifib.net/share/private/","xmlUrl":"https://softinst184170.host.vifib.net/public/feed","htmlUrl":"https://softinst184170.host.vifib.net/public/feed","version":"RSS","type":"rss","title":"theia0","text":"theia0","portal_type":"Opml Outline","parent_id":"f185fe2fa2280798a181e3fa45d2cad763d10540","parent_url":"https://softinst184163.host.vifib.net/public/feeds","reference":"62bcecf32735232bf9b4fde41698bbe743dcd438","active":true,"dateCreated":"Thu, 04 Jul 2024 09:00:47 +0000","dateModified":"Thu, 04 Jul 2024 18:48:28 +0000","opml_title":"ERP5 Playground instance"}},{"id":"/1/0/0","value":{},"doc":{"url":"https://softinst184163.host.vifib.net/share/private/","xmlUrl":"https://softinst184163.host.vifib.net/public/feed","htmlUrl":"https://softinst184163.host.vifib.net/public/feed","version":"RSS","type":"rss","title":"ERP5 Playground instance","text":"ERP5 Playground instance","portal_type":"Opml Outline","parent_id":"f185fe2fa2280798a181e3fa45d2cad763d10540","parent_url":"https://softinst184163.host.vifib.net/public/feeds","reference":"305ddc8bed6419578add7a4cb19a0960a9b8a89d","active":true,"dateCreated":"Thu, 04 Jul 2024 09:00:47 +0000","dateModified":"Thu, 04 Jul 2024 18:48:28 +0000","opml_title":"ERP5 Playground instance"}}]',
MOCK_PROMISE = '{"data":{"rows":[{"id":"/0","value":{},"doc":{"docs":"http://blogs.law.harvard.edu/tech/rss","generator":"PyRSS2Gen-1.1.0","lastBuildDate":"Thu, 04 Jul 2024 23:04:14 GMT","description":"ERP5 Playground instance","link":"https://softinst220514.host.vifib.net/public/feed","title":"PBS COMP-3557-theia-1"}},{"id":"/0/0","value":{},"doc":{"source":"check-free-disk-space","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:12:11+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBjaGVjay1mcmVlLWRpc2stc3BhY2UsIDAyYTA1MWVmMjYwYWM3Y2RiNDE5ZmMwOTljNDRlM2U3LCAyMDI0LTA1LTEyVDA1OjEyOjExKzAwMDA=","guid_isPermaLink":"false","category":"OK","description":"Current disk usage: OKEnable to display disk space predictions: False","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] check-free-disk-space"}},{"id":"/0/1","value":{},"doc":{"source":"ssh-to-COMP-3557-slappart27-theia-1-pull","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-07-04T20:42:10+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBzc2gtdG8tQ09NUC0zNTU3LXNsYXBwYXJ0MjctdGhlaWEtMS1wdWxsLCBkNDFkOGNkOThmMDBiMjA0ZTk4MDA5OThlY2Y4NDI3ZSwgMjAyNC0wNy0wNFQyMDo0MjoxMCswMDAw","guid_isPermaLink":"false","category":"OK","description":"","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] ssh-to-COMP-3557-slappart27-theia-1-pull"}},{"id":"/0/2","value":{},"doc":{"source":"buildout-slappart46-status","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:46+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBidWlsZG91dC1zbGFwcGFydDQ2LXN0YXR1cywgODY1MzBjNTE5MmIwMzA1MjlhODJiZTY1NDZkMWI5MmYsIDIwMjQtMDUtMTJUMDU6MzA6NDYrMDAwMA==","guid_isPermaLink":"false","category":"OK","description":"buildout is OK","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] buildout-slappart46-status"}},{"id":"/0/3","value":{},"doc":{"source":"notifier-feed-check-malformed-or-failure","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:45+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBub3RpZmllci1mZWVkLWNoZWNrLW1hbGZvcm1lZC1vci1mYWlsdXJlLCA1NTZiNThmNzE3ZjY5Yjk0M2Q1NTc1YTZjNGU3M2U2YiwgMjAyNC0wNS0xMlQwNTozMDo0NSswMDAw","guid_isPermaLink":"false","category":"OK","description":"OK /srv/slapgrid/slappart46/bin/notifier-feed-check-malformed-or-failure.py run with success","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] notifier-feed-check-malformed-or-failure"}},{"id":"/0/4","value":{},"doc":{"source":"monitor-httpd-listening-on-tcp","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:46+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBtb25pdG9yLWh0dHBkLWxpc3RlbmluZy1vbi10Y3AsIDhkYmE4OTAyMTcxNzRhNzdhMzM5NWVkNjI0MWE0Y2Y5LCAyMDI0LTA1LTEyVDA1OjMwOjQ2KzAwMDA=","guid_isPermaLink":"false","category":"OK","description":"non-authenticated request to https://[2001:67c:1254:103::5975]:8070 succeeded (returned expected code 401)","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] monitor-httpd-listening-on-tcp"}},{"id":"/0/5","value":{},"doc":{"source":"ssh-to-COMP-3557-slappart27-theia-1-push","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-07-04T22:54:25+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBzc2gtdG8tQ09NUC0zNTU3LXNsYXBwYXJ0MjctdGhlaWEtMS1wdXNoLCBkNDFkOGNkOThmMDBiMjA0ZTk4MDA5OThlY2Y4NDI3ZSwgMjAyNC0wNy0wNFQyMjo1NDoyNSswMDAw","guid_isPermaLink":"false","category":"OK","description":"","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] ssh-to-COMP-3557-slappart27-theia-1-push"}},{"id":"/0/6","value":{},"doc":{"source":"check-monitor-frontend-password","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:46+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBjaGVjay1tb25pdG9yLWZyb250ZW5kLXBhc3N3b3JkLCA1YWRjMTAzMDhiYWVhYWJmMWZhZDIyMDBjOTQ0ODc2OSwgMjAyNC0wNS0xMlQwNTozMDo0NiswMDAw","guid_isPermaLink":"false","category":"OK","description":"authenticated request to https://softinst220514.host.vifib.net succeeded (returned expected code 200)","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] check-monitor-frontend-password"}},{"id":"/0/7","value":{},"doc":{"source":"stalled-pull-push","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:46+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBzdGFsbGVkLXB1bGwtcHVzaCwgZWI3Mjc5NWJlM2IzYTQwNjg1Nzg3YTk0NWFmMTcyZjEsIDIwMjQtMDUtMTJUMDU6MzA6NDYrMDAwMA==","guid_isPermaLink":"false","category":"OK","description":"OK /srv/slapgrid/slappart46/bin/stalled-pull-push run with success","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] stalled-pull-push"}},{"id":"/0/8","value":{},"doc":{"source":"monitor-bootstrap-status","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:46+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBtb25pdG9yLWJvb3RzdHJhcC1zdGF0dXMsIGY2MjBiYTUxMjc4Y2U3YjIxZjlhNzg1ZDc4OThkZThjLCAyMDI0LTA1LTEyVDA1OjMwOjQ2KzAwMDA=","guid_isPermaLink":"false","category":"OK","description":"Bootstrap OK","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] monitor-bootstrap-status"}},{"id":"/0/9","value":{},"doc":{"source":"monitor-http-frontend","source_url":"https://softinst220514.host.vifib.net/share/public/","pubDate":"2024-05-12T05:30:45+0000","guid":"RVJQNSBQbGF5Z3JvdW5kIGluc3RhbmNlLCBtb25pdG9yLWh0dHAtZnJvbnRlbmQsIDNiMTcwYjc0Njk0MTNjMTUxZTkxMmU5MDI4ZjFjNWVkLCAyMDI0LTA1LTEyVDA1OjMwOjQ1KzAwMDA=","guid_isPermaLink":"false","category":"OK","description":"non-authenticated request to https://softinst220514.host.vifib.net succeeded (returned expected code 401)","link":"https://softinst220514.host.vifib.net/share/private/","title":"[OK] monitor-http-frontend"}}],"total_rows":11}}',
MOCK_INSTANCE = '{"data":{"rows":[{"id":"monitor.global","value":{},"doc":{"status":"OK","state":{"error":0,"success":21},"type":"global","portal_type":"Software Instance","date":"2024-07-04T23:10:20+0000","_links":{"rss_url":{"href":"https://softinst184178.host.vifib.net/public/feed"},"public_url":{"href":"https://softinst184178.host.vifib.net/share/public/"},"private_url":{"href":"https://softinst184178.host.vifib.net/share/private/"},"related_monitor":[]},"data":{"state":"monitor_state.data","process_state":"monitor_process_resource.status","process_resource":"monitor_resource_process.data","memory_resource":"monitor_resource_memory.data","io_resource":"monitor_resource_io.data","monitor_process_state":"monitor_resource.status"},"title":"theia1","specialise_title":"ERP5 Playground instance","aggregate_reference":"COMP-3490","ipv4":"10.0.225.11","ipv6":"2001:67c:1254:f1::f6f0","software_release":"https://lab.nexedi.com/nexedi/slapos/raw/1.0.358/software/theia/software.cfg","software_type":"import","partition_id":"slappart30","parameters":[{"key":"","title":"monitor-user","value":"admin"},{"key":"monitor-password","title":"monitor-password","value":"CQo1zXbIB0XqAwYt"},{"key":"min-free-disk-MB","title":"min-free-disk-MB","value":""}]}}],"total_rows":1}}',
OPML_ATTACHMENT_NAME = "__opml__",
PROMISE_TYPE = "Promise",
SOFTWARE_INSTANCE_TYPE = "Software Instance",
INSTANCE_TREE_TYPE = "Instance Tree",
OPML_PORTAL_TYPE = "Opml",
ZONE_LIST = [
"-1200",
"-1100",
"-1000",
"-0900",
"-0800",
"-0700",
"-0600",
"-0500",
"-0400",
"-0300",
"-0200",
"-0100",
"+0000",
"+0100",
"+0200",
"+0300",
"+0400",
"+0500",
"+0600",
"+0700",
"+0800",
"+0900",
"+1000",
"+1100",
"+1200"
];
function generateHash(str) {
return rusha.digestFromString(str);
}
function createStorage(context, storage_spec, key) {
var signature;
signature = generateHash(JSON.stringify(storage_spec));
if (!context._remote_storage_dict.hasOwnProperty(key) ||
signature !== context._remote_storage_dict[key].signature) {
context._remote_storage_dict[key] = {
storage: jIO.createJIO(storage_spec),
signature: signature
};
}
return context._remote_storage_dict[key].storage;
}
/**
* The JIO OPML Tree Replicated Storage extension for monitor
*
* @class ReplicatedOPMLStorage
* @constructor
*/
function ReplicatedOPMLStorage(spec) {
if (typeof spec.type !== 'string') {
throw new TypeError(
"ReplicatedOPMLStorage 'type' is not a string"
);
}
if (spec.local_sub_storage === undefined) {
throw new TypeError("ReplicatedOPMLStorage 'local_sub_storage' " +
"is not defined");
}
this._local_sub_storage = jIO.createJIO(spec.local_sub_storage);
this._local_sub_storage_spec = spec.local_sub_storage;
if (spec.remote_sub_storage !== undefined) {
this._remote_sub_storage_spec = spec.remote_sub_storage;
this._remote_sub_storage = jIO.createJIO(spec.remote_sub_storage);
}
this._remote_storage_unreachable_status =
spec.remote_storage_unreachable_status;
this._remote_storage_dict = {};
this._remote_parser_storage_type = spec.remote_parser_storage_type;
if (this._remote_parser_storage_type === undefined) {
this._remote_parser_storage_type = "parser";
}
this._remote_opml_check_time_interval =
spec.remote_opml_check_time_interval;
if (this._remote_opml_check_time_interval === undefined) {
// one day in miliseconds
this._remote_opml_check_time_interval = 86400000;
}
this._request_timeout = spec.request_timeout;
if (this._request_timeout === undefined) {
this._request_timeout = 0; // no timeout
}
}
ReplicatedOPMLStorage.prototype.get = function () {
return this._local_sub_storage.get.apply(this._local_sub_storage,
arguments);
};
ReplicatedOPMLStorage.prototype.buildQuery = function () {
return this._local_sub_storage.buildQuery.apply(this._local_sub_storage,
arguments);
};
ReplicatedOPMLStorage.prototype.put = function (id, doc) {
//allow app configuration types (forms, views, actions, etc)
/*if (!doc.hasOwnProperty('portal_type') || doc.portal_type !== 'opml') {
throw new TypeError("Cannot put object which portal_type is not 'opml'");
}*/
if (doc.active === undefined) {
doc.active = true;
}
return this._local_sub_storage.put(id, doc);
};
ReplicatedOPMLStorage.prototype.hasCapacity = function (capacity) {
var this_storage_not_capacity_list = ['post', 'getAttachment', 'putAttachment'];
if (this_storage_not_capacity_list.indexOf(capacity) !== -1) {
return false;
}
if (capacity === 'include') {
return true;
}
return this._local_sub_storage.hasCapacity.apply(this._local_sub_storage,
arguments);
};
ReplicatedOPMLStorage.prototype.allAttachments = function () {
return this._local_sub_storage.allAttachments.apply(this._local_sub_storage,
arguments);
};
ReplicatedOPMLStorage.prototype.remove = function (id) {
//the removal of an opml involves to remove the full opml tree asociated
//note: if the opml belongs to a slapos master, next sync will restore it
var storage = this._local_sub_storage;
return storage.get(id)
.push(function (doc) {
if (doc.portal_type !== OPML_PORTAL_TYPE) {
return storage.remove(id);
}
function removeOPMLTree(url) {
var remove_id_list = [],
remove_signature_id_list = [];
// remove related instance tree
remove_id_list.push(generateHash(id));
// removed saved opml content
remove_signature_id_list.push({
id: url,
name: url
});
// remove all related documents
return storage.allDocs({
select_list: ["xmlUrl", "url"],
query: '(portal_type:"Opml Outline") AND (parent_url:"' + url + '")'
})
.push(function (document_result) {
var i,
query_list = [];
for (i = 0; i < document_result.data.total_rows; i += 1) {
query_list.push('(parent_id:"' +
document_result.data.rows[i].id + '")');
remove_id_list.push(document_result.data.rows[i].id);
remove_signature_id_list.push({
id: document_result.data.rows[i].id,
name: document_result.data.rows[i].value.xmlUrl
});
remove_signature_id_list.push({
id: document_result.data.rows[i].id,
name: document_result.data.rows[i].value.url
});
}
// cleanup all sub opml items
if (query_list.length > 0) {
return storage.allDocs({query: query_list.join(" OR ")});
}
return {data: {total_rows: 0}};
})
.push(function (sub_item_result) {
var j,
i,
k,
remove_queue = new RSVP.Queue();
function removeItem(key) {
remove_queue
.push(function () {
return storage.remove(key);
});
}
function removeAttachmentItem(id, name) {
remove_queue
.push(function () {
return storage.removeAttachment(id, name);
})
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return undefined;
}
throw error;
});
}
// remove signatures
for (k = 0; k < remove_signature_id_list.length; k += 1) {
removeAttachmentItem(
remove_signature_id_list[k].id,
remove_signature_id_list[k].name
);
}
// remove opml-outline sub-items (rss)
for (j = 0; j < sub_item_result.data.total_rows; j += 1) {
removeItem(sub_item_result.data.rows[j].id);
}
// remove opml-outline
for (i = 0; i < remove_id_list.length; i += 1) {
removeItem(remove_id_list[i]);
}
return remove_queue;
})
.push(function () {
return storage.remove(url);
});
}
return removeOPMLTree(id);
});
};
function getStorageUrl(storage_spec) {
var spec = storage_spec;
while (spec !== undefined) {
if (spec.url !== undefined) {
return spec.url;
}
if (spec.document_id !== undefined) {
return spec.document_id;
}
spec = spec.sub_storage;
}
throw new Error("No url found on sub storage: " +
JSON.stringify(storage_spec));
}
function getDocumentAsAttachment(context, attachment_id, name) {
return context._local_sub_storage.getAttachment(attachment_id, name)
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return undefined;
}
console.error(error);
})
.push(function (attachment) {
if (attachment) {
return jIO.util.readBlobAsText(attachment)
.then(function (evt) {
return JSON.parse(evt.target.result);
});
}
return {};
});
}
function fixGlobalInstanceDocument(instance) {
// Fix some property as backed is old, to keep backward compatibility
// XXX - this method should be removed when all backend will be upgraded
if (instance._embedded !== undefined) {
if (instance._embedded.instance !== undefined) {
// set aggregate_reference to the computer reference and make it
// searchable
instance.aggregate_reference = instance._embedded.instance.computer;
}
if (instance._embedded.hasOwnProperty('promises')) {
// remove useless information from the document
delete instance._embedded.promises;
}
}
if (instance.hasOwnProperty('hosting-title')) {
// hosting-title should be specialise_title
instance.specialise_title = instance['hosting-title'];
delete instance['hosting-title'];
}
// set portal_type is not defined
if (!instance.hasOwnProperty('portal_type')) {
instance.portal_type = SOFTWARE_INSTANCE_TYPE;
}
return instance;
}
function updateSubStorageStatus(context, signature_dict, next_status) {
var key,
update_status_queue = new RSVP.Queue();
function updateStatus(id) {
update_status_queue
.push(function () {
return context._local_sub_storage.get(id);
})
.push(function (doc) {
if (doc.portal_type === PROMISE_TYPE) {
doc.category = next_status;
return context._local_sub_storage.put(id, doc);
}
if (doc.status !== undefined) {
doc.status = next_status;
return context._local_sub_storage.put(id, doc);
}
});
}
for (key in signature_dict) {
if (signature_dict.hasOwnProperty(key)) {
if (signature_dict[key].status !== next_status) {
updateStatus(key);
signature_dict[key].status = next_status;
}
}
}
return update_status_queue
.push(function () {
return signature_dict;
});
}
function loadSubStorage(context, storage_spec, parent_id, index, type) {
var sub_storage,
result_dict,
storage_key,
url;
url = getStorageUrl(storage_spec);
storage_key = generateHash(parent_id + url);
sub_storage = createStorage(context, storage_spec, storage_key);
result_dict = {
parent_id: parent_id,
type: type || storage_spec.type,
current_signature: {},
result: {data: {total_rows: 0}},
url: url,
parent_index: index
};
return sub_storage.allDocs({include_docs: true})
.push(undefined, function (error) {
//throw error;
console.error(error);
if (mock_test) {
if (index === 2) {
return JSON.parse(MOCK_PROMISE);
}
if (index === 3) {
return JSON.parse(MOCK_INSTANCE);
}
}
return undefined;
})
.push(function (result) {
if (result === undefined) {
if (context._remote_storage_unreachable_status !== undefined) {
// update status of local documents
// and set unreachable status
return getDocumentAsAttachment(context, parent_id, url)
.push(function (signature_document) {
return updateSubStorageStatus(
context,
signature_document,
context._remote_storage_unreachable_status
);
})
.push(function (signature_dict) {
return signature_dict;
});
}
return {};
}
result_dict.result = result;
return getDocumentAsAttachment(
context,
parent_id,
url
);
})
.push(function (signature_document) {
result_dict.current_signature = signature_document;
return result_dict;
});
}
function updateInstanceTreeState(hosting, element) {
var status = element.status.toUpperCase();
if (hosting.instance_amount === 0) {
hosting.status_date = fixDateTimezone(element.date);
}
if (hosting.status === "ERROR") {
return;
} else if (status === "ERROR") {
hosting.status = status;
} else if (status === "WARNING") {
hosting.status = status;
}
if (status === "OK" && hosting.status !== status) {
hosting.status = status;
}
}
function fixDateTimezone(date_string) {
// set default timezone offset to UTC
// XXX should be removed later
/*if (ZONE_LIST.indexOf(date_string.slice(-5)) === -1) {
return date_string + "+0000";
}*/
return date_string;
}
function getOpmlTree(context, opml_url, opml_spec, basic_login, opml_title, slapos_master_url) {
var opml_storage,
opml_document_list = [],
delete_key_list = [],
attachment_document_list = [],
opml_result_list,
current_signature_dict = {},
fetch_remote_opml = false,
instance_tree,
id;
id = generateHash(opml_url);
opml_storage = createStorage(context, opml_spec, id);
// Instance Tree is build from OPML and it has status
instance_tree = {
title: opml_title || "",
portal_type: INSTANCE_TREE_TYPE,
opml_url: opml_url,
status: "WARNING",
instance_amount: 0,
status_date: (new Date()).toUTCString() + "+0000",
slapos_master_url: slapos_master_url
};
return getDocumentAsAttachment(context, opml_url, OPML_ATTACHMENT_NAME)
.push(function (opml_doc) {
var current_time = new Date().getTime();
if (opml_doc.expire_time !== undefined) {
fetch_remote_opml = (opml_doc.expire_time - current_time) < 0;
} else {
fetch_remote_opml = true;
}
if (fetch_remote_opml) {
return opml_storage.allDocs({include_docs: true})
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return {data: {total_rows: 0}};
}
//throw error;
console.error(error);
return {data: {total_rows: 0}};
})
.push(function (opml_result) {
opml_result_list = opml_result;
if (opml_result.data.total_rows > 0) {
attachment_document_list.push({
id: opml_url,
name: OPML_ATTACHMENT_NAME,
doc: {
expire_time: new Date().getTime() +
context._remote_opml_check_time_interval,
data: opml_result
}
});
return getDocumentAsAttachment(
context,
id,
opml_url
);
}
return {};
})
.push(function (signature_dict) {
current_signature_dict = signature_dict;
});
}
opml_result_list = opml_doc.data;
})
.push(function () {
var i,
item,
signature,
doc_signature_dict = {},
skip_add = false,
id_hash,
result_list = [],
header_dict = {};
if (opml_result_list.data.total_rows > 0) {
if (opml_result_list.data.rows[0].doc.title) {
instance_tree.title = opml_result_list.data.rows[0]
.doc.title;
}
if (fetch_remote_opml) {
header_dict = {
dateCreated: opml_result_list.data.rows[0].doc.dateCreated,
dateModified: opml_result_list.data.rows[0].doc.dateModified,
opml_title: opml_result_list.data.rows[0].doc.title
};
}
}
var iter = opml_result_list.data.total_rows;
if (mock_test) {
opml_result_list.data.rows = JSON.parse(MOCK_OPMLS);
opml_result_list.data.total_rows = 6;
iter = 4;
}
for (i = 1; i < iter; i += 1) {
item = opml_result_list.data.rows[i];
if (item.doc.xmlUrl !== undefined) {
id_hash = generateHash(id + item.id);
result_list.push(loadSubStorage(
context,
{
type: context._remote_parser_storage_type,
document_id: item.doc.xmlUrl,
attachment_id: 'enclosure',
parser: 'rss',
sub_storage: {
type: "http",
timeout: context._request_timeout
}
},
id_hash,
i,
PROMISE_TYPE
));
// Load private docs
if (item.doc.url !== undefined) {
result_list.push(loadSubStorage(
context,
{
type: 'webhttp',
url: item.doc.url.replace('jio_private', 'private'),
basic_login: basic_login,
timeout: context._request_timeout
},
id_hash,
i
));
}
if (fetch_remote_opml) {
// Append this document signature to the list
signature = generateHash(JSON.stringify(item.doc));
doc_signature_dict[id_hash] = {
signature: signature
};
if (current_signature_dict.hasOwnProperty(id_hash)) {
if (current_signature_dict[id_hash].signature === signature) {
// remote document was not modified, delete and skip add
delete current_signature_dict[id_hash];
skip_add = true;
}
delete current_signature_dict[id_hash];
}
Object.assign(item.doc, {
portal_type: "Opml Outline",
parent_id: id,
parent_url: opml_url,
reference: id_hash,
active: true,
slapos_master_url: slapos_master_url
});
Object.assign(item.doc, header_dict);
if (!skip_add) {
opml_document_list.push({
id: id_hash,
doc: item.doc
});
}
}
}
}
if (fetch_remote_opml && Object.keys(doc_signature_dict).length > 0) {
attachment_document_list.push({
id: opml_url,
name: opml_url,
doc: doc_signature_dict
});
delete_key_list.push.apply(delete_key_list,
Object.keys(current_signature_dict));
}
return RSVP.all(result_list);
})
.push(function (result_list) {
var i,
j,
start,
extra_dict;
function applyItemToTree(item, item_result, extra_dict) {
var id_hash,
element = item.doc,
signature,
item_id = item.guid || item.id,
status = (element.status || element.category),
item_signature_dict = {};
if (element.type === 'global') {
updateInstanceTreeState(instance_tree, element);
instance_tree.instance_amount += 1;
if (element.aggregate_reference === undefined) {
// XXX - document need to be updated to keep compatibility
element = fixGlobalInstanceDocument(element);
}
// XXX - fixing date timezone
element.date = fixDateTimezone(element.date);
}
// XXX - fixing date timezone
if (element.pubDate !== undefined) {
element.pubDate = fixDateTimezone(element.pubDate);
}
id_hash = generateHash(item_result.parent_id +
item_result.url + item_id);
if (extra_dict !== undefined) {
Object.assign(element, extra_dict);
}
// Generating document signature
signature = generateHash(JSON.stringify(element));
item_signature_dict[id_hash] = {
signature: signature,
status: status
};
if (item_result.current_signature.hasOwnProperty(id_hash)) {
if (item_result.current_signature[id_hash].signature ===
signature) {
// the document was not modified return
delete item_result.current_signature[id_hash];
return;
}
// the document exists and has changed
delete item_result.current_signature[id_hash];
}
Object.assign(element, {
parent_id: item_result.parent_id,
portal_type: element.portal_type || element.type ||
item_result.type,
status: status,
reference: element.reference || id_hash,
active: true,
slapos_master_url: slapos_master_url
});
opml_document_list.push({
id: id_hash,
doc: element
});
attachment_document_list.push({
id: item_result.parent_id,
name: item_result.url,
doc: item_signature_dict
});
}
for (i = 0; i < result_list.length; i += 1) {
extra_dict = undefined;
start = 0;
if (result_list[i].result.data.total_rows > 0) {
if (result_list[i].type === PROMISE_TYPE) {
// the first element of rss is the header
extra_dict = {
lastBuildDate: fixDateTimezone(result_list[i].result.data
.rows[0].doc.lastBuildDate),
channel: result_list[i].result.data.rows[0].doc.description,
channel_item: result_list[i].result.data.rows[0].doc.title
};
start = 1;
}
for (j = start; j < result_list[i].result.data.total_rows; j += 1) {
applyItemToTree(
result_list[i].result.data.rows[j],
result_list[i],
extra_dict
);
}
delete_key_list.push.apply(
delete_key_list,
Object.keys(result_list[i].current_signature)
);
} else if (Object.keys(result_list[i].current_signature).length > 0) {
// if the remote data is empty and current_signature is not empty,
// push to storage in case the status was changed
// this help for speed optimisation
attachment_document_list.push({
id: result_list[i].parent_id,
name: result_list[i].url,
doc: result_list[i].current_signature
});
} else if (context._remote_storage_unreachable_status !== undefined) {
if (result_list[i].type === "webhttp") {
// In case it was impossible to get software Instance
// Add an empty Software Instance with unreachable status
applyItemToTree(
{
id: "monitor.global",
doc: {
portal_type: SOFTWARE_INSTANCE_TYPE,
status: context._remote_storage_unreachable_status,
title: opml_result_list.data.rows[result_list[i]
.parent_index].doc.title,
date: new Date().toUTCString() + "+0000",
specialise_title: opml_result_list.data.rows[result_list[i]
.parent_index].doc.opml_title
}
},
result_list[i],
undefined
);
}
}
}
opml_document_list.push({
id: id,
doc: instance_tree
});
return [opml_document_list, delete_key_list, attachment_document_list];
});
}
function pushDocumentToStorage(context, document_list, delete_key_list,
attachment_document_list) {
var document_queue = new RSVP.Queue(),
i;
function pushDocument(id, element) {
document_queue
.push(function () {
return context._local_sub_storage.put(id, element);
});
}
for (i = 0; i < document_list.length; i += 1) {
pushDocument(
document_list[i].id,
document_list[i].doc
);
}
return document_queue
.push(function () {
var k,
remove_queue = new RSVP.Queue();
// remove all document which were not updated
function removeDocument(key) {
remove_queue
.push(function () {
return context._local_sub_storage.remove(key);
})
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return {};
}
throw error;
});
}
for (k = 0; k < delete_key_list.length; k += 1) {
removeDocument(delete_key_list[k]);
}
return remove_queue;
})
.push(function () {
var j,
signature_queue = new RSVP.Queue();
function pushAttachment(id, name, element) {
signature_queue
.push(function () {
return context._local_sub_storage.putAttachment(
id,
name,
new Blob([JSON.stringify(element)], {type : 'application/json'})
);
})
.push(undefined, function (error) {
console.error(error);
});
}
for (j = 0; j < attachment_document_list.length; j += 1) {
pushAttachment(
attachment_document_list[j].id,
attachment_document_list[j].name,
attachment_document_list[j].doc
);
}
});
}
function syncOpmlStorage(context) {
return context._local_sub_storage.allDocs({
query: '(portal_type:"' + OPML_PORTAL_TYPE + '") AND (active:true) AND (url:"https://%")',
select_list: ["title", "url", "basic_login", "slapos_master_url"]
})
.push(function (storage_result) {
var i,
opml_queue = new RSVP.Queue();
function syncFullOpml(storage_spec) {
opml_queue
.push(function () {
return getOpmlTree(
context,
storage_spec.url,
{
type: context._remote_parser_storage_type,
document_id: storage_spec.url,
attachment_id: 'enclosure',
parser: 'opml',
sub_storage: {
type: "http",
timeout: context._request_timeout
}
},
storage_spec.basic_login,
storage_spec.title,
storage_spec.slapos_master_url
);
})
.push(function (result_list) {
return pushDocumentToStorage(
context,
result_list[0],
result_list[1],
result_list[2]
);
});
}
for (i = 0; i < storage_result.data.total_rows; i += 1) {
syncFullOpml(storage_result.data.rows[i].value);
}
return opml_queue;
});
}
ReplicatedOPMLStorage.prototype.repair = function () {
var context = this,
has_failed = false,
error_msg = "",
argument_list = arguments;
function getParameterDictFromUrl(uri_param) {
if (uri_param.has('url') && uri_param.has('password') &&
uri_param.has('username') && uri_param.get('url').startsWith('http')) {
return {
opml_url: uri_param.get('url').trim(),
username: uri_param.get('username').trim(),
password: uri_param.get('password').trim()
};
}
}
function getParameterFromconnectionDict(connection_dict) {
if (connection_dict["monitor-url"] &&
connection_dict["monitor-url"].startsWith('http') &&
connection_dict["monitor-user"] &&
connection_dict["monitor-password"]) {
return {
opml_url: connection_dict["monitor-url"].trim(),
username: connection_dict["monitor-user"].trim(),
password: connection_dict["monitor-password"].trim()
};
}
}
function readMonitoringParameter(parmeter_xml) {
var parser = new DOMParser(),
xml_doc = parser.parseFromString(parmeter_xml, "text/xml"),
parameter,
uri_param,
json_parameter,
parameter_dict,
monitor_dict = {};
json_parameter = xml_doc.getElementById("_");
if (json_parameter !== undefined && json_parameter !== null) {
parameter_dict = JSON.parse(json_parameter.textContent);
if (parameter_dict.hasOwnProperty("monitor-setup-url")) {
return getParameterDictFromUrl(
new URLSearchParams(parameter_dict["monitor-setup-url"])
);
}
return getParameterFromconnectionDict(parameter_dict);
}
parameter = xml_doc.getElementById("monitor-setup-url");
if (parameter !== undefined && parameter !== null) {
// monitor-setup-url exists
uri_param = new URLSearchParams(parameter.textContent);
return getParameterDictFromUrl(uri_param);
}
parameter = xml_doc.getElementById("monitor-url");
if (parameter !== undefined && parameter !== null) {
monitor_dict.url = parameter.textContent.trim();
parameter = xml_doc.getElementById("monitor-user");
if (parameter === undefined && parameter !== null) {
return;
}
monitor_dict.username = parameter.textContent.trim();
parameter = xml_doc.getElementById("monitor-password");
if (parameter === undefined && parameter !== null) {
return;
}
monitor_dict.password = parameter.textContent.trim();
return monitor_dict;
}
}
function getInstanceOPMLList(storage) {
if (!storage) {
return [];
}
var instance_tree_list = [],
opml_list = [],
uid_dict = {};
return storage.allDocs({
query: '(portal_type:"Instance Tree") AND (validation_state:"validated")',
select_list: ['title', 'default_successor_uid', 'uid', 'slap_state', 'id']
})
.push(function (result) {
var i, slapos_id, slapos_master_url = "",
uid_search_list = [];
for (i = 0; i < result.data.total_rows; i += 1) {
if (result.data.rows[i].value.slap_state !== "destroy_requested") {
//TODO could slapos_id be used to desambiguate identic title
//instances trees between different storages?
slapos_id = result.data.rows[i].value.title;
if (result.data.rows[i].master_url) {
slapos_master_url = result.data.rows[i].master_url;
}
instance_tree_list.push({
title: result.data.rows[i].value.title,
relative_url: result.data.rows[i].id,
slapos_id: slapos_id,
slapos_master_url: slapos_master_url,
active: (result.data.rows[i].value.slap_state ===
"start_requested") ? true : false,
state: (result.data.rows[i].value.slap_state ===
"start_requested") ? "Started" : "Stopped"
});
uid_search_list.push(result.data.rows[i].value.uid);
if (result.data.rows[i].value.default_successor_uid) {
uid_dict[result.data.rows[i].value.default_successor_uid] = i;
}
}
}
return storage.allDocs({
query: '(portal_type:"Software Instance") AND ' +
'(successor_related_uid:("' + uid_search_list.join('","') + '"))',
select_list: ['uid', 'successor_related_uid', 'connection_xml']
});
})
.push(function (result) {
var i,
tmp_parameter,
tmp_uid,
slapos_master_url = "";
for (i = 0; i < result.data.total_rows; i += 1) {
tmp_uid = result.data.rows[i].value.uid;
if (uid_dict.hasOwnProperty(tmp_uid)) {
tmp_parameter = readMonitoringParameter(result.data.rows[i].value.connection_xml);
if (tmp_parameter === undefined) {
tmp_parameter = {username: "", password: "", opml_url: undefined};
}
if (instance_tree_list[uid_dict[tmp_uid]]) {
if (result.data.rows[i].master_url) {
slapos_master_url = result.data.rows[i].master_url;
}
opml_list.push({
portal_type: OPML_PORTAL_TYPE,
title: instance_tree_list[uid_dict[tmp_uid]]
.title,
relative_url: instance_tree_list[uid_dict[tmp_uid]]
.relative_url,
url: tmp_parameter.opml_url || String(tmp_uid) + " NO MONITOR",
has_monitor: tmp_parameter.opml_url !== undefined,
username: tmp_parameter.username,
password: tmp_parameter.password,
basic_login: btoa(tmp_parameter.username + ':' +
tmp_parameter.password),
active: tmp_parameter.opml_url !== undefined &&
instance_tree_list[uid_dict[tmp_uid]].active,
state: instance_tree_list[uid_dict[tmp_uid]].state,
slapos_master_url: slapos_master_url
});
}
}
}
return opml_list;
});
}
function cleanOpmlStorage(context) {
//TODO use slapos_master_url in the query instead of iterate later
return context._local_sub_storage.allDocs({
query: '(portal_type:"' + OPML_PORTAL_TYPE + '")',
select_list: ["title", "url", "basic_login", "slapos_master_url", "manually_added"]
})
.push(function (result) {
function removeAllOPML(remove_opml_list, jio) {
var remove_queue = new RSVP.Queue(), i;
function remove_opml(id) {
remove_queue
.push(function () {
return jio.remove(id);
});
}
for (i = 0; i < remove_opml_list.length; i += 1) {
remove_opml(remove_opml_list[i].id);
}
return remove_queue;
}
var slapos_master_url_list = [], spec_list = context._remote_sub_storage_spec.storage_list,
i, remove_opml_list = [], opml_list = result.data.rows;
if (spec_list) {
for (i = 0; i < spec_list.length; i += 1) {
if (spec_list[i].sub_storage && spec_list[i].sub_storage.url) {
slapos_master_url_list.push(spec_list[i].sub_storage.url);
}
}
}
if (slapos_master_url_list.length > 0) {
for (i = 0; i < opml_list.length; i += 1) {
if (!opml_list[i].value.manually_added) {
if (!slapos_master_url_list.includes(opml_list[i].value.slapos_master_url)) {
remove_opml_list.push(opml_list[i]);
}
}
}
}
return RSVP.all([
removeAllOPML(remove_opml_list, context)
]);
});
}
return new RSVP.Queue()
//repair sub storage layers (local and remote)
.push(function () {
return context._local_sub_storage.repair.apply(
context._local_sub_storage,
argument_list
);
})
.push(function () {
if (context._remote_sub_storage) {
return context._remote_sub_storage.repair.apply(
context._remote_sub_storage,
argument_list
);
}
})
.push(function () {
//delete all opmls trees of no longer present slapos masters
//(in case master url list was updated)
return cleanOpmlStorage(context);
})
.push(function () {
//get opml list from remote slapos master(s)
return getInstanceOPMLList(context._remote_sub_storage);
})
.push(undefined, function (error) {
has_failed = true;
if (error.target) {
if (error.target.status === 401) {
error_msg = ": unauthorized access to slapos master";
}
if (error.target.status === 404) {
error_msg = ": slapos master url not found";
}
if (error.target.status - 500 > 0) {
error_msg = ": server error on slapos master side - " + error.target.status;
}
if (error.target.responseURL) {
error_msg += ". URL: " + error.target.responseURL;
}
} else {
console.log(error);
}
throw "Failed to import remote configurations" + error_msg;
})
.push(function (opml_list) {
//HARDCODED second master
if (mock_test) {
if (context._remote_sub_storage.__storage._storage_list[1]) {
if (context._remote_sub_storage.__storage._storage_list[1].__storage._storage_definition.url === "https://softinst239021.host.vifib.net/erp5/web_site_module/slapos_hateoas/") {
opml_list[6].slapos_master_url = "https://softinst239021.host.vifib.net/erp5/web_site_module/slapos_hateoas/";
}
} else {
opml_list.pop();
}
}
//store opmls in local sub storage
var i, push_queue = new RSVP.Queue();
function pushOPML(opml_dict) {
push_queue
.push(function () {
return context._local_sub_storage.put(opml_dict.url, opml_dict);
});
}
for (i = 0; i < opml_list.length; i += 1) {
pushOPML(opml_list[i]);
}
return push_queue;
})
.push(function () {
if (!has_failed) {
//sync storage using updated opml list
return syncOpmlStorage(context);
}
});
};
jIO.addStorage('replicatedopml', ReplicatedOPMLStorage);
}(jIO, RSVP, DOMParser, Rusha, Blob, console, btoa, URLSearchParams));
\ No newline at end of file
/*
* Copyright 2016, Nexedi SA
* Released under the LGPL license.
* http://www.gnu.org/licenses/lgpl.html
*/
/*jslint nomen: true*/
/*global jIO, RSVP */
(function (jIO, RSVP) {
"use strict";
function ajax(storage, options) {
if (options === undefined) {
options = {};
}
if (storage._authorization !== undefined) {
if (options.headers === undefined) {
options.headers = {};
}
options.headers.Authorization = storage._authorization;
}
if (storage._with_credentials !== undefined) {
if (options.xhrFields === undefined) {
options.xhrFields = {};
}
options.xhrFields.withCredentials = storage._with_credentials;
}
if (storage._timeout !== undefined) {
options.timeout = storage._timeout;
}
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax(options);
});
}
function restrictDocumentId(id) {
var slash_index = id.indexOf("/");
if (slash_index !== 0 && slash_index !== -1) {
throw new jIO.util.jIOError("id " + id + " is forbidden (no begin /)",
400);
}
if (id.lastIndexOf("/") === (id.length - 1)) {
throw new jIO.util.jIOError("id " + id + " is forbidden (no end /)",
400);
}
return id;
}
function getJsonDocument(context, id) {
return new RSVP.Queue()
.push(function () {
return ajax(context, {
type: "GET",
url: context._url + "/" + id + ".json",
dataType: "text"
});
})
.push(function (response) {
return {id: id, doc: JSON.parse(response.target.responseText)};
}, function (error) {
if ((error.target !== undefined) &&
(error.target.status === 404)) {
throw new jIO.util.jIOError("Cannot find document '" + id + "'", 404);
}
throw error;
});
}
/**
* The JIO WEB HTTP Storage extension
*
* @class WEBHTTPStorage
* @constructor
*/
function WEBHTTPStorage(spec) {
if (typeof spec.url !== 'string') {
throw new TypeError("WEBHTTPStorage 'url' is not of type string");
}
this._url = spec.url.replace(new RegExp("[/]+$"), "");
if (typeof spec.basic_login === 'string') {
this._authorization = "Basic " + spec.basic_login;
}
this._with_credentials = spec.with_credentials;
this._timeout = spec.timeout;
}
WEBHTTPStorage.prototype.get = function (id) {
var context = this;
id = restrictDocumentId(id);
return getJsonDocument(context, id)
.push(function (element) {
return element.doc;
}, function (error) {
if ((error.target !== undefined) &&
(error.target.status === 404)) {
throw new jIO.util.jIOError("Cannot find document", 404);
}
throw error;
});
};
WEBHTTPStorage.prototype.hasCapacity = function (capacity) {
return (capacity === "list") || (capacity === "include");
};
WEBHTTPStorage.prototype.buildQuery = function (options) {
var context = this,
item_list = [],
push_item;
if (options.include_docs === true) {
push_item = function (id, item) {
item_list.push({
"id": id,
"value": {},
"doc": item
});
};
} else {
push_item = function (id) {
item_list.push({
"id": id,
"value": {}
});
};
}
return new RSVP.Queue()
.push(function () {
return ajax(context, {
type: "GET",
url: context._url + "/_document_list",
dataType: "text"
});
})
.push(function (response) {
var document_list = [],
promise_list = [],
i;
document_list = response.target.responseText.split('\n');
for (i = 0; i < document_list.length; i += 1) {
if (document_list[i]) {
promise_list.push(getJsonDocument(context, document_list[i]));
}
}
return RSVP.all(promise_list);
}, function (error) {
if ((error.target !== undefined) &&
(error.target.status === 404)) {
throw new jIO.util.jIOError("Cannot find document", 404);
}
throw error;
})
.push(function (result_list) {
var i;
for (i = 0; i < result_list.length; i += 1) {
push_item(result_list[i].id, result_list[i].doc);
}
return item_list;
});
};
jIO.addStorage('webhttp', WEBHTTPStorage);
}(jIO, RSVP));
\ No newline at end of file
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