1
0
Fork 0

Merge branch 'devel' of https://github.com/triAGENS/ArangoDB into devel

This commit is contained in:
Jan Steemann 2014-05-09 00:42:32 +02:00
commit 20f1940458
9 changed files with 419 additions and 29 deletions

View File

@ -195,10 +195,6 @@ Handler::status_t RestReplicationHandler::execute() {
goto BAD_CALL; goto BAD_CALL;
} }
if (isCoordinatorError()) {
return status_t(Handler::HANDLER_DONE);
}
handleCommandRestoreCollection(); handleCommandRestoreCollection();
} }
else if (command == "restore-indexes") { else if (command == "restore-indexes") {
@ -206,10 +202,6 @@ Handler::status_t RestReplicationHandler::execute() {
goto BAD_CALL; goto BAD_CALL;
} }
if (isCoordinatorError()) {
return status_t(Handler::HANDLER_DONE);
}
handleCommandRestoreIndexes(); handleCommandRestoreIndexes();
} }
else if (command == "restore-data") { else if (command == "restore-data") {
@ -219,7 +211,7 @@ Handler::status_t RestReplicationHandler::execute() {
#ifdef TRI_ENABLE_CLUSTER #ifdef TRI_ENABLE_CLUSTER
if (ServerState::instance()->isCoordinator()) { if (ServerState::instance()->isCoordinator()) {
handleTrampolineCoordinator(); handleCommandRestoreDataCoordinator();
} }
else { else {
handleCommandRestoreData(); handleCommandRestoreData();
@ -1941,7 +1933,20 @@ void RestReplicationHandler::handleCommandRestoreCollection () {
TRI_server_id_t remoteServerId = 0; // TODO TRI_server_id_t remoteServerId = 0; // TODO
string errorMsg; string errorMsg;
int res = processRestoreCollection(json, overwrite, recycleIds, force, remoteServerId, errorMsg); #ifdef TRI_ENABLE_CLUSTER
int res;
if (ServerState::instance()->isCoordinator()) {
res = processRestoreCollectionCoordinator(json, overwrite, recycleIds,
force, remoteServerId, errorMsg);
}
else {
res = processRestoreCollection(json, overwrite, recycleIds, force,
remoteServerId, errorMsg);
}
#else
int res = processRestoreCollection(json, overwrite, recycleIds, force,
remoteServerId, errorMsg);
#endif
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
@ -1982,7 +1987,17 @@ void RestReplicationHandler::handleCommandRestoreIndexes () {
TRI_server_id_t remoteServerId = 0; // TODO TRI_server_id_t remoteServerId = 0; // TODO
string errorMsg; string errorMsg;
#ifdef TRI_ENABLE_CLUSTER
int res;
if (ServerState::instance()->isCoordinator()) {
res = processRestoreIndexesCoordinator(json, force, remoteServerId, errorMsg);
}
else {
res = processRestoreIndexes(json, force, remoteServerId, errorMsg);
}
#else
int res = processRestoreIndexes(json, force, remoteServerId, errorMsg); int res = processRestoreIndexes(json, force, remoteServerId, errorMsg);
#endif
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
@ -2124,6 +2139,135 @@ int RestReplicationHandler::processRestoreCollection (TRI_json_t const* collecti
return TRI_ERROR_NO_ERROR; return TRI_ERROR_NO_ERROR;
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief restores the structure of a collection, coordinator case
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_ENABLE_CLUSTER
int RestReplicationHandler::processRestoreCollectionCoordinator (
TRI_json_t const* collection,
bool dropExisting,
bool reuseId,
bool force,
TRI_server_id_t remoteServerId,
string& errorMsg) {
if (! JsonHelper::isArray(collection)) {
errorMsg = "collection declaration is invalid";
return TRI_ERROR_HTTP_BAD_PARAMETER;
}
TRI_json_t const* parameters = JsonHelper::getArrayElement(collection, "parameters");
if (! JsonHelper::isArray(parameters)) {
errorMsg = "collection parameters declaration is invalid";
return TRI_ERROR_HTTP_BAD_PARAMETER;
}
TRI_json_t const* indexes = JsonHelper::getArrayElement(collection, "indexes");
if (! JsonHelper::isList(indexes)) {
errorMsg = "collection indexes declaration is invalid";
return TRI_ERROR_HTTP_BAD_PARAMETER;
}
const string name = JsonHelper::getStringValue(parameters, "name", "");
if (name.empty()) {
errorMsg = "collection name is missing";
return TRI_ERROR_HTTP_BAD_PARAMETER;
}
if (JsonHelper::getBooleanValue(parameters, "deleted", false)) {
// we don't care about deleted collections
return TRI_ERROR_NO_ERROR;
}
TRI_vocbase_col_t* col = 0;
if (reuseId) {
TRI_json_t const* idString = JsonHelper::getArrayElement(parameters, "cid");
if (! JsonHelper::isString(idString)) {
errorMsg = "collection id is missing";
return TRI_ERROR_HTTP_BAD_PARAMETER;
}
TRI_voc_cid_t cid = StringUtils::uint64(idString->_value._string.data, idString->_value._string.length - 1);
// first look up the collection by the cid
col = TRI_LookupCollectionByIdVocBase(_vocbase, cid);
}
if (col == 0) {
// not found, try name next
col = TRI_LookupCollectionByNameVocBase(_vocbase, name.c_str());
}
// drop an existing collection if it exists
if (col != 0) {
if (dropExisting) {
int res = TRI_DropCollectionVocBase(_vocbase, col, remoteServerId);
if (res == TRI_ERROR_FORBIDDEN) {
// some collections must not be dropped
// instead, truncate them
CollectionNameResolver resolver(_vocbase);
SingleCollectionWriteTransaction<EmbeddableTransaction<RestTransactionContext>, UINT64_MAX> trx(_vocbase, resolver, col->_cid);
res = trx.begin();
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
TRI_barrier_t* barrier = TRI_CreateBarrierElement(&(trx.primaryCollection()->_barrierList));
if (barrier == 0) {
return TRI_ERROR_INTERNAL;
}
res = trx.truncate(false);
res = trx.finish(res);
TRI_FreeBarrier(barrier);
return res;
}
if (res != TRI_ERROR_NO_ERROR) {
errorMsg = "unable to drop collection '" + name + "': " + string(TRI_errno_string(res));
return res;
}
}
else {
int res = TRI_ERROR_ARANGO_DUPLICATE_NAME;
errorMsg = "unable to create collection '" + name + "': " + string(TRI_errno_string(res));
return res;
}
}
// now re-create the collection
int res = createCollection(parameters, &col, reuseId, remoteServerId);
if (res != TRI_ERROR_NO_ERROR) {
errorMsg = "unable to create collection: " + string(TRI_errno_string(res));
return res;
}
return TRI_ERROR_NO_ERROR;
}
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief restores the indexes of a collection TODO MOVE /// @brief restores the indexes of a collection TODO MOVE
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -2226,6 +2370,112 @@ int RestReplicationHandler::processRestoreIndexes (TRI_json_t const* collection,
return TRI_ERROR_NO_ERROR; return TRI_ERROR_NO_ERROR;
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief restores the indexes of a collection, coordinator case
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_ENABLE_CLUSTER
int RestReplicationHandler::processRestoreIndexesCoordinator (
TRI_json_t const* collection,
bool force,
TRI_server_id_t remoteServerId,
string& errorMsg) {
if (! JsonHelper::isArray(collection)) {
errorMsg = "collection declaration is invalid";
return TRI_ERROR_HTTP_BAD_PARAMETER;
}
TRI_json_t const* parameters = JsonHelper::getArrayElement(collection, "parameters");
if (! JsonHelper::isArray(parameters)) {
errorMsg = "collection parameters declaration is invalid";
return TRI_ERROR_HTTP_BAD_PARAMETER;
}
TRI_json_t const* indexes = JsonHelper::getArrayElement(collection, "indexes");
if (! JsonHelper::isList(indexes)) {
errorMsg = "collection indexes declaration is invalid";
return TRI_ERROR_HTTP_BAD_PARAMETER;
}
const size_t n = indexes->_value._objects._length;
if (n == 0) {
// nothing to do
return TRI_ERROR_NO_ERROR;
}
const string name = JsonHelper::getStringValue(parameters, "name", "");
if (name.empty()) {
errorMsg = "collection name is missing";
return TRI_ERROR_HTTP_BAD_PARAMETER;
}
if (JsonHelper::getBooleanValue(parameters, "deleted", false)) {
// we don't care about deleted collections
return TRI_ERROR_NO_ERROR;
}
// look up the collection
TRI_vocbase_col_t* col = TRI_LookupCollectionByNameVocBase(_vocbase, name.c_str());
if (col == 0) {
errorMsg = "could not find collection '" + name + "'";
return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND;
}
int res = TRI_UseCollectionVocBase(_vocbase, col);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
TRI_primary_collection_t* primary = col->_collection;
TRI_ReadLockReadWriteLock(&_vocbase->_inventoryLock);
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(primary);
for (size_t i = 0; i < n; ++i) {
TRI_json_t const* idxDef = (TRI_json_t const*) TRI_AtVector(&indexes->_value._objects, i);
TRI_index_t* idx = 0;
// {"id":"229907440927234","type":"hash","unique":false,"fields":["x","Y"]}
res = TRI_FromJsonIndexDocumentCollection((TRI_document_collection_t*) primary, idxDef, &idx);
if (res != TRI_ERROR_NO_ERROR) {
errorMsg = "could not create index: " + string(TRI_errno_string(res));
break;
}
else {
assert(idx != 0);
res = TRI_SaveIndex(primary, idx, remoteServerId);
if (res != TRI_ERROR_NO_ERROR) {
errorMsg = "could not save index: " + string(TRI_errno_string(res));
break;
}
}
}
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(primary);
TRI_ReadUnlockReadWriteLock(&_vocbase->_inventoryLock);
TRI_ReleaseCollectionVocBase(_vocbase, col);
return TRI_ERROR_NO_ERROR;
}
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief apply the data from a collection dump or the continuous log /// @brief apply the data from a collection dump or the continuous log
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -2569,6 +2819,63 @@ void RestReplicationHandler::handleCommandRestoreData () {
} }
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief restores the data of a collection, coordinator case
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_ENABLE_CLUSTER
void RestReplicationHandler::handleCommandRestoreDataCoordinator () {
char const* value = _request->value("collection");
if (value == 0) {
generateError(HttpResponse::BAD,
TRI_ERROR_HTTP_BAD_PARAMETER,
"invalid collection parameter");
return;
}
CollectionNameResolver resolver(_vocbase);
TRI_voc_cid_t cid = resolver.getCollectionId(value);
if (cid == 0) {
generateError(HttpResponse::BAD,
TRI_ERROR_HTTP_BAD_PARAMETER,
"invalid collection parameter");
return;
}
bool recycleIds = false;
value = _request->value("recycleIds");
if (value != 0) {
recycleIds = StringUtils::boolean(value);
}
bool force = false;
value = _request->value("force");
if (value != 0) {
force = StringUtils::boolean(value);
}
TRI_server_id_t remoteServerId = 0; // TODO
string errorMsg;
int res = processRestoreData(resolver, cid, remoteServerId, recycleIds, force, errorMsg);
if (res != TRI_ERROR_NO_ERROR) {
generateError(HttpResponse::SERVER_ERROR, res);
}
else {
TRI_json_t result;
TRI_InitArrayJson(TRI_CORE_MEM_ZONE, &result);
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE, &result, "result", TRI_CreateBooleanJson(TRI_CORE_MEM_ZONE, true));
generateResult(&result);
}
}
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief dumps the data of a collection /// @brief dumps the data of a collection
/// ///

View File

@ -234,6 +234,19 @@ namespace triagens {
TRI_server_id_t, TRI_server_id_t,
std::string&); std::string&);
////////////////////////////////////////////////////////////////////////////////
/// @brief restores the structure of a collection, coordinator case
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_ENABLE_CLUSTER
int processRestoreCollectionCoordinator (struct TRI_json_s const*,
bool,
bool,
bool,
TRI_server_id_t,
std::string&);
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief restores the indexes of a collection TODO MOVE /// @brief restores the indexes of a collection TODO MOVE
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -243,6 +256,17 @@ namespace triagens {
TRI_server_id_t, TRI_server_id_t,
std::string&); std::string&);
////////////////////////////////////////////////////////////////////////////////
/// @brief restores the indexes of a collection, coordinator case
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_ENABLE_CLUSTER
int processRestoreIndexesCoordinator (struct TRI_json_s const*,
bool,
TRI_server_id_t,
std::string&);
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief apply a single marker from the collection dump /// @brief apply a single marker from the collection dump
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -283,6 +307,14 @@ namespace triagens {
void handleCommandRestoreData (); void handleCommandRestoreData ();
////////////////////////////////////////////////////////////////////////////////
/// @brief handle a restore command for a specific collection, coordinator case
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_ENABLE_CLUSTER
void handleCommandRestoreDataCoordinator ();
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief handle a dump command for a specific collection /// @brief handle a dump command for a specific collection
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -377,7 +377,7 @@ static string GetArangoVersion () {
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief fetch the version from the server /// @brief check if server is a coordinator of a cluster
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static bool GetArangoIsCluster () { static bool GetArangoIsCluster () {

View File

@ -142,6 +142,12 @@ static bool RecycleIds = false;
static bool Force = false; static bool Force = false;
////////////////////////////////////////////////////////////////////////////////
/// @brief cluster mode flag
////////////////////////////////////////////////////////////////////////////////
static bool clusterMode = false;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief statistics /// @brief statistics
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -364,6 +370,47 @@ static string GetArangoVersion () {
return version; return version;
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief check if server is a coordinator of a cluster
////////////////////////////////////////////////////////////////////////////////
static bool GetArangoIsCluster () {
map<string, string> headers;
string command = "return ArangoServerState.role();";
SimpleHttpResult* response = Client->request(HttpRequest::HTTP_REQUEST_POST,
"/_admin/execute?returnAsJSON=true",
command.c_str(),
command.size(),
headers);
if (response == 0 || ! response->isComplete()) {
if (response != 0) {
delete response;
}
return false;
}
string role = "UNDEFINED";
if (response->getHttpReturnCode() == HttpResponse::OK) {
// default value
role.assign(response->getBody().c_str(), response->getBody().length());
}
else {
if (response->wasHttpError()) {
Client->setErrorMessage(GetHttpErrorMessage(response), false);
}
Connection->disconnect();
}
delete response;
return role == "\"COORDINATOR\"";
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief send the request to re-create a collection /// @brief send the request to re-create a collection
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -450,8 +497,7 @@ static int SendRestoreIndexes (TRI_json_t const* json,
/// @brief send the request to load data into a collection /// @brief send the request to load data into a collection
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static int SendRestoreData (string const& cid, static int SendRestoreData (string const& cname,
string const& cname,
char const* buffer, char const* buffer,
size_t bufferSize, size_t bufferSize,
string& errorMsg) { string& errorMsg) {
@ -743,7 +789,7 @@ static int ProcessInputDirectory (string& errorMsg) {
Stats._totalBatches++; Stats._totalBatches++;
int res = SendRestoreData(cid, cname, buffer.begin(), length, errorMsg); int res = SendRestoreData(cname, buffer.begin(), length, errorMsg);
if (res != TRI_ERROR_NO_ERROR) { if (res != TRI_ERROR_NO_ERROR) {
TRI_CLOSE(fd); TRI_CLOSE(fd);
@ -933,6 +979,10 @@ int main (int argc, char* argv[]) {
} }
} }
if (major >= 2) {
// Version 1.4 did not yet have a cluster mode
clusterMode = GetArangoIsCluster();
}
if (Progress) { if (Progress) {
cout << "Connected to ArangoDB '" << BaseClient.endpointServer()->getSpecification() << endl; cout << "Connected to ArangoDB '" << BaseClient.endpointServer()->getSpecification() << endl;

View File

@ -245,6 +245,7 @@ var modalDialogHelper = modalDialogHelper || {};
i, i,
id = idPre + idPost, id = idPre + idPost,
lastId = 1, lastId = 1,
buttonTh = document.createElement("th"),
addLineButton = document.createElement("button"), addLineButton = document.createElement("button"),
input = document.createElement("input"), input = document.createElement("input"),
addNewLine = function(content) { addNewLine = function(content) {
@ -252,6 +253,7 @@ var modalDialogHelper = modalDialogHelper || {};
var innerTr = document.createElement("tr"), var innerTr = document.createElement("tr"),
innerLabelTh = document.createElement("th"), innerLabelTh = document.createElement("th"),
innerContentTh = document.createElement("th"), innerContentTh = document.createElement("th"),
innerButtonTh = document.createElement("th"),
innerInput = document.createElement("input"), innerInput = document.createElement("input"),
removeRow = document.createElement("button"), removeRow = document.createElement("button"),
lastItem; lastItem;
@ -276,14 +278,15 @@ var modalDialogHelper = modalDialogHelper || {};
table.removeChild(innerTr); table.removeChild(innerTr);
rows.splice(rows.indexOf(innerTr), 1 ); rows.splice(rows.indexOf(innerTr), 1 );
}; };
innerButtonTh.appendChild(removeRow);
innerContentTh.appendChild(removeRow); innerTr.appendChild(innerButtonTh);
rows.push(innerTr); rows.push(innerTr);
}; };
input.type = "text"; input.type = "text";
input.id = id + "_1"; input.id = id + "_1";
contentTh.appendChild(input); contentTh.appendChild(input);
contentTh.appendChild(addLineButton); buttonTh.appendChild(addLineButton);
tr.appendChild(buttonTh);
addLineButton.onclick = function() { addLineButton.onclick = function() {
addNewLine(); addNewLine();
}; };
@ -309,7 +312,7 @@ var modalDialogHelper = modalDialogHelper || {};
// Set Classnames and attributes. // Set Classnames and attributes.
div.id = idprefix + "modal"; div.id = idprefix + "modal";
div.className = "modal hide fade"; div.className = "modal hide fade createModalDialog";
div.setAttribute("tabindex", "-1"); div.setAttribute("tabindex", "-1");
div.setAttribute("role", "dialog"); div.setAttribute("role", "dialog");
div.setAttribute("aria-labelledby", "myModalLabel"); div.setAttribute("aria-labelledby", "myModalLabel");

View File

@ -5,7 +5,6 @@
text-align: left; text-align: left;
width: 20% !important; width: 20% !important;
input,
select, select,
textarea { textarea {
margin-top: 10px; margin-top: 10px;

View File

@ -42,6 +42,7 @@
margin-bottom: 10px; margin-bottom: 10px;
margin-top: 10px; margin-top: 10px;
} }
} }
.icon-info-sign { .icon-info-sign {

View File

@ -3424,8 +3424,8 @@ pre.gv-object-view {
margin-left: 0; } margin-left: 0; }
.dashboard-interior-chart { .dashboard-interior-chart {
height: 222px; background-color: white;
background-color: white; } height: 222px; }
.dashboard-large-chart { .dashboard-large-chart {
height: 250px; height: 250px;
@ -4276,7 +4276,6 @@ input.gv-radio-button {
font-weight: 400 !important; font-weight: 400 !important;
text-align: left; text-align: left;
width: 20% !important; } width: 20% !important; }
.collectionTh input,
.collectionTh select, .collectionTh select,
.collectionTh textarea { .collectionTh textarea {
margin-top: 10px; } margin-top: 10px; }

View File

@ -37,7 +37,6 @@
"frontend/js/templates/editListEntryView.ejs", "frontend/js/templates/editListEntryView.ejs",
"frontend/js/templates/footerView.ejs", "frontend/js/templates/footerView.ejs",
"frontend/js/templates/foxxActiveView.ejs", "frontend/js/templates/foxxActiveView.ejs",
"frontend/js/templates/foxxEditView.ejs",
"frontend/js/templates/foxxInstalledView.ejs", "frontend/js/templates/foxxInstalledView.ejs",
"frontend/js/templates/graphManagementView.ejs", "frontend/js/templates/graphManagementView.ejs",
"frontend/js/templates/graphView.ejs", "frontend/js/templates/graphView.ejs",
@ -260,12 +259,6 @@
"cluster.js": { "cluster.js": {
"files": [ "files": [
"clusterFrontend/js/collections/arangoClusterStatisticsCollection.js",
"clusterFrontend/js/collections/clusterCollections.js",
"clusterFrontend/js/collections/clusterCoordinators.js",
"clusterFrontend/js/collections/clusterDatabases.js",
"clusterFrontend/js/collections/clusterServers.js",
"clusterFrontend/js/collections/clusterShards.js",
"clusterFrontend/js/models/clusterCollection.js", "clusterFrontend/js/models/clusterCollection.js",
"clusterFrontend/js/models/clusterCoordinator.js", "clusterFrontend/js/models/clusterCoordinator.js",
"clusterFrontend/js/models/clusterDatabase.js", "clusterFrontend/js/models/clusterDatabase.js",
@ -273,6 +266,12 @@
"clusterFrontend/js/models/clusterServer.js", "clusterFrontend/js/models/clusterServer.js",
"clusterFrontend/js/models/clusterShard.js", "clusterFrontend/js/models/clusterShard.js",
"clusterFrontend/js/models/clusterType.js", "clusterFrontend/js/models/clusterType.js",
"clusterFrontend/js/collections/arangoClusterStatisticsCollection.js",
"clusterFrontend/js/collections/clusterCollections.js",
"clusterFrontend/js/collections/clusterCoordinators.js",
"clusterFrontend/js/collections/clusterDatabases.js",
"clusterFrontend/js/collections/clusterServers.js",
"clusterFrontend/js/collections/clusterShards.js",
"frontend/js/models/arangoDocument.js", "frontend/js/models/arangoDocument.js",
"frontend/js/models/arangoStatistics.js", "frontend/js/models/arangoStatistics.js",
"frontend/js/models/arangoStatisticsDescription.js", "frontend/js/models/arangoStatisticsDescription.js",