diff --git a/js/actions/api-cluster.js b/js/actions/api-cluster.js index 09604ae865..9123e20ecc 100644 --- a/js/actions/api-cluster.js +++ b/js/actions/api-cluster.js @@ -96,12 +96,39 @@ actions.defineHttp({ return; } - if (node.Role !== 'Coordinator') { + if (node.Role !== 'Coordinator' && node.Role !== 'DBServer') { actions.resultError(req, res, actions.HTTP_BAD, - 'only coordinators can be removed at this time'); + 'unhandled role ' + node.role); return; } + let preconditions = {}; + preconditions['/arango/Supervision/Health/' + serverId + '/Status'] = {'old': 'FAILED'}; + // need to make sure it is not responsible for anything + if (node.Role == 'DBServer') { + let used = []; + preconditions = reducePlanServers(function(data, agencyKey, servers) { + data[agencyKey] = {'old': servers}; + if (servers.indexOf(serverId) !== -1) { + used.push(agencyKey); + } + return data; + }, {}); + preconditions = reduceCurrentServers(function(data, agencyKey, servers) { + data[agencyKey] = {'old': servers}; + if (servers.indexOf(serverId) !== -1) { + used.push(agencyKey); + } + return data; + }, preconditions); + + if (used.length > 0) { + actions.resultError(req, res, actions.HTTP_PRECONDITION_FAILED, + 'the server is still in use at the following locations: ' + JSON.stringify(used)); + return; + } + } + let operations = {}; operations['/arango/Coordinators/' + serverId] = {'op': 'delete'}; operations['/arango/DBServers/' + serverId] = {'op': 'delete'}; @@ -109,9 +136,6 @@ actions.defineHttp({ operations['/arango/Supervision/Health/' + serverId] = {'op': 'delete'}; operations['/arango/MapUniqueToShortID/' + serverId] = {'op': 'delete'}; - let preconditions = {}; - preconditions['/arango/Supervision/Health/' + serverId + '/Status'] = {'old': 'FAILED'}; - try { global.ArangoAgency.write([[operations, preconditions]]); } catch (e) { @@ -572,42 +596,68 @@ actions.defineHttp({ } }); +function reducePlanServers(reducer, data) { + var databases = ArangoAgency.get('Plan/Collections'); + databases = databases.arango.Plan.Collections; + + return Object.keys(databases).reduce(function(data, databaseName) { + var collections = databases[databaseName]; + + return Object.keys(collections).reduce(function(data, collectionKey) { + var collection = collections[collectionKey]; + + return Object.keys(collection.shards).reduce(function(data, shardKey) { + var servers = collection.shards[shardKey]; + + let key = '/arango/Plan/Collections/' + databaseName + '/' + collectionKey + '/shards/' + shardKey; + return reducer(data, key, servers); + }, data); + }, data); + }, data); +} + +function reduceCurrentServers(reducer, data) { + var databases = ArangoAgency.get('Current/Collections'); + databases = databases.arango.Current.Collections; + + return Object.keys(databases).reduce(function(data, databaseName) { + var collections = databases[databaseName]; + + return Object.keys(collections).reduce(function(data, collectionKey) { + var collection = collections[collectionKey]; + + return Object.keys(collection).reduce(function(data, shardKey) { + var servers = collection[shardKey].servers; + + let key = '/arango/Current/Collections/' + databaseName + '/' + collectionKey + '/' + shardKey + '/servers'; + return reducer(data, key, servers); + }, data); + }, data); + }, data); +} + // ////////////////////////////////////////////////////////////////////////////// // / @brief changes responsibility for all shards from oldServer to newServer. // / This needs to be done atomically! // ////////////////////////////////////////////////////////////////////////////// function changeAllShardReponsibilities (oldServer, newServer) { - // This is only called when we have the write lock and we "only" have to - // make sure that either all or none of the shards are moved. - var databases = ArangoAgency.get('Plan/Collections'); - databases = databases.arango.Plan.Collections; - - let operations = {}; - let preconditions = {}; - Object.keys(databases).forEach(function(databaseName) { - var collections = databases[databaseName]; - - Object.keys(collections).forEach(function(collectionKey) { - var collection = collections[collectionKey]; - - Object.keys(collection.shards).forEach(function (shardKey) { - var servers = collection.shards[shardKey]; - var oldServers = _.cloneDeep(servers); - servers = servers.map(function(server) { - if (server === oldServer) { - return newServer; - } else { - return server; - } - }); - let key = '/arango/Plan/Collections/' + databaseName + '/' + collectionKey + '/shards/' + shardKey; - operations[key] = servers; - preconditions[key] = {'old': oldServers}; - }); + return reducePlanServers(function(data, key, servers) { + var oldServers = _.cloneDeep(servers); + servers = servers.map(function(server) { + if (server === oldServer) { + return newServer; + } else { + return server; + } }); + data.operations[key] = servers; + data.preconditions[key] = {'old': oldServers}; + return data; + }, { + operations: {}, + preconditions: {}, }); - return {operations, preconditions}; } // //////////////////////////////////////////////////////////////////////////////