// s/commands_admin.cpp /** * Copyright (C) 2008 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ /* TODO _ concurrency control. _ limit() works right? _ KillCursors later _ secondary indexes */ #include "pch.h" #include "../util/message.h" #include "../util/processinfo.h" #include "../util/stringutils.h" #include "../util/version.h" #include "../client/connpool.h" #include "../db/dbmessage.h" #include "../db/commands.h" #include "../db/stats/counters.h" #include "config.h" #include "chunk.h" #include "grid.h" #include "strategy.h" #include "stats.h" #include "writeback_listener.h" #include "client.h" namespace mongo { namespace dbgrid_cmds { class GridAdminCmd : public Command { public: GridAdminCmd( const char * n ) : Command( n , false, tolowerString(n).c_str() ) { } virtual bool slaveOk() const { return true; } virtual bool adminOnly() const { return true; } // all grid commands are designed not to lock virtual LockType locktype() const { return NONE; } }; // --------------- misc commands ---------------------- class NetStatCmd : public GridAdminCmd { public: NetStatCmd() : GridAdminCmd("netstat") { } virtual void help( stringstream& help ) const { help << " shows status/reachability of servers in the cluster"; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { result.append("configserver", configServer.getPrimary().getConnString() ); result.append("isdbgrid", 1); return true; } } netstat; class ServerStatusCmd : public Command { public: ServerStatusCmd() : Command( "serverStatus" , true ) { _started = time(0); } virtual bool slaveOk() const { return true; } virtual LockType locktype() const { return NONE; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) { result.append( "host" , prettyHostName() ); result.append("version", versionString); result.append("process","mongos"); result.append("uptime",(double) (time(0)-_started)); result.appendDate( "localTime" , jsTime() ); { BSONObjBuilder t( result.subobjStart( "mem" ) ); ProcessInfo p; if ( p.supported() ) { t.appendNumber( "resident" , p.getResidentSize() ); t.appendNumber( "virtual" , p.getVirtualMemorySize() ); t.appendBool( "supported" , true ); } else { result.append( "note" , "not all mem info support on this platform" ); t.appendBool( "supported" , false ); } t.done(); } { BSONObjBuilder bb( result.subobjStart( "connections" ) ); bb.append( "current" , connTicketHolder.used() ); bb.append( "available" , connTicketHolder.available() ); bb.done(); } { BSONObjBuilder bb( result.subobjStart( "extra_info" ) ); bb.append("note", "fields vary by platform"); ProcessInfo p; p.getExtraInfo(bb); bb.done(); } result.append( "opcounters" , globalOpCounters.getObj() ); { BSONObjBuilder bb( result.subobjStart( "ops" ) ); bb.append( "sharded" , opsSharded.getObj() ); bb.append( "notSharded" , opsNonSharded.getObj() ); bb.done(); } result.append( "shardCursorType" , shardedCursorTypes.getObj() ); { BSONObjBuilder asserts( result.subobjStart( "asserts" ) ); asserts.append( "regular" , assertionCount.regular ); asserts.append( "warning" , assertionCount.warning ); asserts.append( "msg" , assertionCount.msg ); asserts.append( "user" , assertionCount.user ); asserts.append( "rollovers" , assertionCount.rollovers ); asserts.done(); } { BSONObjBuilder bb( result.subobjStart( "network" ) ); networkCounter.append( bb ); bb.done(); } return 1; } time_t _started; } cmdServerStatus; class FsyncCommand : public GridAdminCmd { public: FsyncCommand() : GridAdminCmd( "fsync" ) {} bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { if ( cmdObj["lock"].trueValue() ) { errmsg = "can't do lock through mongos"; return false; } BSONObjBuilder sub; bool ok = true; int numFiles = 0; vector shards; Shard::getAllShards( shards ); for ( vector::iterator i=shards.begin(); i!=shards.end(); i++ ) { Shard s = *i; BSONObj x = s.runCommand( "admin" , "fsync" ); sub.append( s.getName() , x ); if ( ! x["ok"].trueValue() ) { ok = false; errmsg = x["errmsg"].String(); } numFiles += x["numFiles"].numberInt(); } result.append( "numFiles" , numFiles ); result.append( "all" , sub.obj() ); return ok; } } fsyncCmd; // ------------ database level commands ------------- class MoveDatabasePrimaryCommand : public GridAdminCmd { public: MoveDatabasePrimaryCommand() : GridAdminCmd("movePrimary") { } virtual void help( stringstream& help ) const { help << " example: { moveprimary : 'foo' , to : 'localhost:9999' }"; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { string dbname = cmdObj.firstElement().valuestrsafe(); if ( dbname.size() == 0 ) { errmsg = "no db"; return false; } if ( dbname == "config" ) { errmsg = "can't move config db"; return false; } DBConfigPtr config = grid.getDBConfig( dbname , false ); if ( ! config ) { errmsg = "can't find db!"; return false; } string to = cmdObj["to"].valuestrsafe(); if ( ! to.size() ) { errmsg = "you have to specify where you want to move it"; return false; } Shard s = Shard::make( to ); if ( config->getPrimary() == s.getConnString() ) { errmsg = "thats already the primary"; return false; } if ( ! grid.knowAboutShard( s.getConnString() ) ) { errmsg = "that server isn't known to me"; return false; } log() << "Moving " << dbname << " primary from: " << config->getPrimary().toString() << " to: " << s.toString() << endl; // Locking enabled now... DistributedLock lockSetup( configServer.getConnectionString(), dbname + "-movePrimary" ); dist_lock_try dlk; // Distributed locking added. try{ dlk = dist_lock_try( &lockSetup , string("Moving primary shard of ") + dbname ); } catch( LockException& e ){ errmsg = string("Error locking distributed lock to move primary shard of ") + dbname + m_caused_by(e); warning() << errmsg << endl; return false; } ScopedDbConnection toconn( s.getConnString() ); // TODO ERH - we need a clone command which replays operations from clone start to now // can just use local.oplog.$main BSONObj cloneRes; bool worked = toconn->runCommand( dbname.c_str() , BSON( "clone" << config->getPrimary().getConnString() ) , cloneRes ); toconn.done(); if ( ! worked ) { log() << "clone failed" << cloneRes << endl; errmsg = "clone failed"; return false; } ScopedDbConnection fromconn( config->getPrimary() ); config->setPrimary( s.getConnString() ); log() << "movePrimary: dropping " << dbname << " from old" << endl; fromconn->dropDatabase( dbname.c_str() ); fromconn.done(); result << "primary " << s.toString(); return true; } } movePrimary; class EnableShardingCmd : public GridAdminCmd { public: EnableShardingCmd() : GridAdminCmd( "enableSharding" ) {} virtual void help( stringstream& help ) const { help << "Enable sharding for a db. (Use 'shardcollection' command afterwards.)\n" << " { enablesharding : \"\" }\n"; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { string dbname = cmdObj.firstElement().valuestrsafe(); if ( dbname.size() == 0 ) { errmsg = "no db"; return false; } DBConfigPtr config = grid.getDBConfig( dbname ); if ( config->isShardingEnabled() ) { errmsg = "already enabled"; return false; } log() << "enabling sharding on: " << dbname << endl; config->enableSharding(); return true; } } enableShardingCmd; // ------------ collection level commands ------------- class ShardCollectionCmd : public GridAdminCmd { public: ShardCollectionCmd() : GridAdminCmd( "shardCollection" ) {} virtual void help( stringstream& help ) const { help << "Shard a collection. Requires key. Optional unique. Sharding must already be enabled for the database.\n" << " { enablesharding : \"\" }\n"; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { string ns = cmdObj.firstElement().valuestrsafe(); if ( ns.size() == 0 ) { errmsg = "no ns"; return false; } DBConfigPtr config = grid.getDBConfig( ns ); if ( ! config->isShardingEnabled() ) { errmsg = "sharding not enabled for db"; return false; } if ( config->isSharded( ns ) ) { errmsg = "already sharded"; return false; } BSONObj key = cmdObj.getObjectField( "key" ); if ( key.isEmpty() ) { errmsg = "no shard key"; return false; } BSONForEach(e, key) { if (!e.isNumber() || e.number() != 1.0) { errmsg = "shard keys must all be ascending"; return false; } } if ( ns.find( ".system." ) != string::npos ) { errmsg = "can't shard system namespaces"; return false; } // Sharding interacts with indexing in at least two ways: // // 1. A unique index must have the sharding key as its prefix. Otherwise maintainig uniqueness would // require coordinated access to all shards. Trying to shard a collection with such an index is not // allowed. // // 2. Sharding a collection requires an index over the sharding key. That index must be create upfront. // The rationale is that sharding a non-empty collection would need to create the index and that could // be slow. Requiring the index upfront allows the admin to plan before sharding and perhaps use // background index construction. One exception to the rule: empty collections. It's fairly easy to // create the index as part of the sharding process. // // We enforce both these conditions in what comes next. { ShardKeyPattern proposedKey( key ); bool hasShardIndex = false; ScopedDbConnection conn( config->getPrimary() ); BSONObjBuilder b; b.append( "ns" , ns ); auto_ptr cursor = conn->query( config->getName() + ".system.indexes" , b.obj() ); while ( cursor->more() ) { BSONObj idx = cursor->next(); // Is index key over the sharding key? Remember that. if ( key.woCompare( idx["key"].embeddedObjectUserCheck() ) == 0 ) { hasShardIndex = true; } // Not a unique index? Move on. if ( idx["unique"].eoo() || ! idx["unique"].trueValue() ) continue; // Shard key is prefix of unique index? Move on. if ( proposedKey.isPrefixOf( idx["key"].embeddedObjectUserCheck() ) ) continue; errmsg = (string)"can't shard collection with unique index on: " + idx.toString(); conn.done(); return false; } BSONObj res = conn->findOne( config->getName() + ".system.namespaces" , BSON( "name" << ns ) ); if ( res["options"].type() == Object && res["options"].embeddedObject()["capped"].trueValue() ) { errmsg = "can't shard capped collection"; conn.done(); return false; } if ( hasShardIndex ) { // make sure there are no null entries in the sharding index BSONObjBuilder cmd; cmd.append( "checkShardingIndex" , ns ); cmd.append( "keyPattern" , key ); BSONObj cmdObj = cmd.obj(); if ( ! conn->runCommand( "admin" , cmdObj , res )) { errmsg = res["errmsg"].str(); conn.done(); return false; } } if ( ! hasShardIndex && ( conn->count( ns ) != 0 ) ) { errmsg = "please create an index over the sharding key before sharding."; conn.done(); return false; } conn.done(); } tlog() << "CMD: shardcollection: " << cmdObj << endl; config->shardCollection( ns , key , cmdObj["unique"].trueValue() ); result << "collectionsharded" << ns; return true; } } shardCollectionCmd; class GetShardVersion : public GridAdminCmd { public: GetShardVersion() : GridAdminCmd( "getShardVersion" ) {} virtual void help( stringstream& help ) const { help << " example: { getShardVersion : 'alleyinsider.foo' } "; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { string ns = cmdObj.firstElement().valuestrsafe(); if ( ns.size() == 0 ) { errmsg = "need to speciy fully namespace"; return false; } DBConfigPtr config = grid.getDBConfig( ns ); if ( ! config->isSharded( ns ) ) { errmsg = "ns not sharded."; return false; } ChunkManagerPtr cm = config->getChunkManager( ns ); if ( ! cm ) { errmsg = "no chunk manager?"; return false; } cm->_printChunks(); result.appendTimestamp( "version" , cm->getVersion().toLong() ); return 1; } } getShardVersionCmd; class SplitCollectionCmd : public GridAdminCmd { public: SplitCollectionCmd() : GridAdminCmd( "split" ) {} virtual void help( stringstream& help ) const { help << " example: - split the shard that contains give key \n" << " { split : 'alleyinsider.blog.posts' , find : { ts : 1 } }\n" << " example: - split the shard that contains the key with this as the middle \n" << " { split : 'alleyinsider.blog.posts' , middle : { ts : 1 } }\n" << " NOTE: this does not move move the chunks, it merely creates a logical seperation \n" ; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { ShardConnection::sync(); string ns = cmdObj.firstElement().valuestrsafe(); if ( ns.size() == 0 ) { errmsg = "no ns"; return false; } DBConfigPtr config = grid.getDBConfig( ns ); if ( ! config->isSharded( ns ) ) { errmsg = "ns not sharded. have to shard before can split"; return false; } BSONObj find = cmdObj.getObjectField( "find" ); if ( find.isEmpty() ) { find = cmdObj.getObjectField( "middle" ); if ( find.isEmpty() ) { errmsg = "need to specify find or middle"; return false; } } ChunkManagerPtr info = config->getChunkManager( ns ); ChunkPtr chunk = info->findChunk( find ); BSONObj middle = cmdObj.getObjectField( "middle" ); assert( chunk.get() ); log() << "splitting: " << ns << " shard: " << chunk << endl; BSONObj res; ChunkPtr p; if ( middle.isEmpty() ) { p = chunk->singleSplit( true /* force a split even if not enough data */ , res ); } else { // sanity check if the key provided is a valid split point if ( ( middle == chunk->getMin() ) || ( middle == chunk->getMax() ) ) { errmsg = "cannot split on initial or final chunk's key"; return false; } vector splitPoints; splitPoints.push_back( middle ); p = chunk->multiSplit( splitPoints , res ); } if ( p.get() == NULL ) { errmsg = "split failed"; result.append( "cause" , res ); return false; } config->getChunkManager( ns , true ); return true; } } splitCollectionCmd; class MoveChunkCmd : public GridAdminCmd { public: MoveChunkCmd() : GridAdminCmd( "moveChunk" ) {} virtual void help( stringstream& help ) const { help << "{ movechunk : 'test.foo' , find : { num : 1 } , to : 'localhost:30001' }"; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { ShardConnection::sync(); Timer t; string ns = cmdObj.firstElement().valuestrsafe(); if ( ns.size() == 0 ) { errmsg = "no ns"; return false; } DBConfigPtr config = grid.getDBConfig( ns ); if ( ! config->isSharded( ns ) ) { errmsg = "ns not sharded. have to shard before can move a chunk"; return false; } BSONObj find = cmdObj.getObjectField( "find" ); if ( find.isEmpty() ) { errmsg = "need to specify find. see help"; return false; } string toString = cmdObj["to"].valuestrsafe(); if ( ! toString.size() ) { errmsg = "you have to specify where you want to move the chunk"; return false; } Shard to = Shard::make( toString ); // so far, chunk size serves test purposes; it may or may not become a supported parameter long long maxChunkSizeBytes = cmdObj["maxChunkSizeBytes"].numberLong(); if ( maxChunkSizeBytes == 0 ) { maxChunkSizeBytes = Chunk::MaxChunkSize; } tlog() << "CMD: movechunk: " << cmdObj << endl; ChunkManagerPtr info = config->getChunkManager( ns ); ChunkPtr c = info->findChunk( find ); const Shard& from = c->getShard(); if ( from == to ) { errmsg = "that chunk is already on that shard"; return false; } BSONObj res; if ( ! c->moveAndCommit( to , maxChunkSizeBytes , res ) ) { errmsg = "move failed"; result.append( "cause" , res ); return false; } // pre-emptively reload the config to get new version info config->getChunkManager( ns , true ); result.append( "millis" , t.millis() ); return true; } } moveChunkCmd; // ------------ server level commands ------------- class ListShardsCmd : public GridAdminCmd { public: ListShardsCmd() : GridAdminCmd("listShards") { } virtual void help( stringstream& help ) const { help << "list all shards of the system"; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { ScopedDbConnection conn( configServer.getPrimary() ); vector all; auto_ptr cursor = conn->query( "config.shards" , BSONObj() ); while ( cursor->more() ) { BSONObj o = cursor->next(); all.push_back( o ); } result.append("shards" , all ); conn.done(); return true; } } listShardsCmd; /* a shard is a single mongod server or a replica pair. add it (them) to the cluster as a storage partition. */ class AddShard : public GridAdminCmd { public: AddShard() : GridAdminCmd("addShard") { } virtual void help( stringstream& help ) const { help << "add a new shard to the system"; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { errmsg.clear(); // get replica set component hosts ConnectionString servers = ConnectionString::parse( cmdObj.firstElement().valuestrsafe() , errmsg ); if ( ! errmsg.empty() ) { log() << "addshard request " << cmdObj << " failed:" << errmsg << endl; return false; } // using localhost in server names implies every other process must use locahost addresses too vector serverAddrs = servers.getServers(); for ( size_t i = 0 ; i < serverAddrs.size() ; i++ ) { if ( serverAddrs[i].isLocalHost() != grid.allowLocalHost() ) { errmsg = "can't use localhost as a shard since all shards need to communicate. " "either use all shards and configdbs in localhost or all in actual IPs " ; log() << "addshard request " << cmdObj << " failed: attempt to mix localhosts and IPs" << endl; return false; } // it's fine if mongods of a set all use default port if ( ! serverAddrs[i].hasPort() ) { serverAddrs[i].setPort( CmdLine::ShardServerPort ); } } // name is optional; addShard will provide one if needed string name = ""; if ( cmdObj["name"].type() == String ) { name = cmdObj["name"].valuestrsafe(); } // maxSize is the space usage cap in a shard in MBs long long maxSize = 0; if ( cmdObj[ ShardFields::maxSize.name() ].isNumber() ) { maxSize = cmdObj[ ShardFields::maxSize.name() ].numberLong(); } if ( ! grid.addShard( &name , servers , maxSize , errmsg ) ) { log() << "addshard request " << cmdObj << " failed: " << errmsg << endl; return false; } result << "shardAdded" << name; return true; } } addServer; /* See usage docs at: * http://www.mongodb.org/display/DOCS/Configuring+Sharding#ConfiguringSharding-Removingashard */ class RemoveShardCmd : public GridAdminCmd { public: RemoveShardCmd() : GridAdminCmd("removeShard") { } virtual void help( stringstream& help ) const { help << "remove a shard to the system."; } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { string target = cmdObj.firstElement().valuestrsafe(); Shard s = Shard::make( target ); if ( ! grid.knowAboutShard( s.getConnString() ) ) { errmsg = "unknown shard"; return false; } ScopedDbConnection conn( configServer.getPrimary() ); // If the server is not yet draining chunks, put it in draining mode. BSONObj searchDoc = BSON( "_id" << s.getName() ); BSONObj drainingDoc = BSON( "_id" << s.getName() << ShardFields::draining(true) ); BSONObj shardDoc = conn->findOne( "config.shards", drainingDoc ); if ( shardDoc.isEmpty() ) { // TODO prevent move chunks to this shard. log() << "going to start draining shard: " << s.getName() << endl; BSONObj newStatus = BSON( "$set" << BSON( ShardFields::draining(true) ) ); conn->update( "config.shards" , searchDoc , newStatus, false /* do no upsert */); errmsg = conn->getLastError(); if ( errmsg.size() ) { log() << "error starting remove shard: " << s.getName() << " err: " << errmsg << endl; return false; } Shard::reloadShardInfo(); result.append( "msg" , "draining started successfully" ); result.append( "state" , "started" ); result.append( "shard" , s.getName() ); conn.done(); return true; } // If the server has been completely drained, remove it from the ConfigDB. // Check not only for chunks but also databases. BSONObj shardIDDoc = BSON( "shard" << shardDoc[ "_id" ].str() ); long long chunkCount = conn->count( "config.chunks" , shardIDDoc ); BSONObj primaryDoc = BSON( "primary" << shardDoc[ "_id" ].str() ); long long dbCount = conn->count( "config.databases" , primaryDoc ); if ( ( chunkCount == 0 ) && ( dbCount == 0 ) ) { log() << "going to remove shard: " << s.getName() << endl; conn->remove( "config.shards" , searchDoc ); errmsg = conn->getLastError(); if ( errmsg.size() ) { log() << "error concluding remove shard: " << s.getName() << " err: " << errmsg << endl; return false; } Shard::removeShard( shardDoc[ "_id" ].str() ); Shard::reloadShardInfo(); result.append( "msg" , "removeshard completed successfully" ); result.append( "state" , "completed" ); result.append( "shard" , s.getName() ); conn.done(); return true; } // If the server is already in draining mode, just report on its progress. // Report on databases (not just chunks) that are left too. result.append( "msg" , "draining ongoing" ); result.append( "state" , "ongoing" ); BSONObjBuilder inner; inner.append( "chunks" , chunkCount ); inner.append( "dbs" , dbCount ); result.append( "remaining" , inner.obj() ); conn.done(); return true; } } removeShardCmd; // --------------- public commands ---------------- class IsDbGridCmd : public Command { public: virtual LockType locktype() const { return NONE; } virtual bool slaveOk() const { return true; } IsDbGridCmd() : Command("isdbgrid") { } bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { result.append("isdbgrid", 1); result.append("hostname", getHostNameCached()); return true; } } isdbgrid; class CmdIsMaster : public Command { public: virtual LockType locktype() const { return NONE; } virtual bool requiresAuth() { return false; } virtual bool slaveOk() const { return true; } virtual void help( stringstream& help ) const { help << "test if this is master half of a replica pair"; } CmdIsMaster() : Command("isMaster" , false , "ismaster") { } virtual bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { result.appendBool("ismaster", true ); result.append("msg", "isdbgrid"); result.appendNumber("maxBsonObjectSize", BSONObjMaxUserSize); return true; } } ismaster; class CmdWhatsMyUri : public Command { public: CmdWhatsMyUri() : Command("whatsmyuri") { } virtual bool logTheOp() { return false; // the modification will be logged directly } virtual bool slaveOk() const { return true; } virtual LockType locktype() const { return NONE; } virtual bool requiresAuth() { return false; } virtual void help( stringstream &help ) const { help << "{whatsmyuri:1}"; } virtual bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { result << "you" << ClientInfo::get()->getRemote(); return true; } } cmdWhatsMyUri; class CmdShardingGetPrevError : public Command { public: virtual LockType locktype() const { return NONE; } virtual bool requiresAuth() { return false; } virtual bool slaveOk() const { return true; } virtual void help( stringstream& help ) const { help << "get previous error (since last reseterror command)"; } CmdShardingGetPrevError() : Command( "getPrevError" , false , "getpreverror") { } virtual bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { errmsg += "getpreverror not supported for sharded environments"; return false; } } cmdGetPrevError; class CmdShardingGetLastError : public Command { public: virtual LockType locktype() const { return NONE; } virtual bool requiresAuth() { return false; } virtual bool slaveOk() const { return true; } virtual void help( stringstream& help ) const { help << "check for an error on the last command executed"; } CmdShardingGetLastError() : Command("getLastError" , false , "getlasterror") { } virtual bool run(const string& dbName, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) { LastError *le = lastError.disableForCommand(); { assert( le ); if ( le->msg.size() && le->nPrev == 1 ) { le->appendSelf( result ); return true; } } ClientInfo * client = ClientInfo::get(); return client->getLastError( cmdObj , result ); } } cmdGetLastError; } class CmdShardingResetError : public Command { public: CmdShardingResetError() : Command( "resetError" , false , "reseterror" ) {} virtual LockType locktype() const { return NONE; } virtual bool requiresAuth() { return false; } virtual bool slaveOk() const { return true; } bool run(const string& dbName , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) { LastError *le = lastError.get(); if ( le ) le->reset(); ClientInfo * client = ClientInfo::get(); set * shards = client->getPrev(); for ( set::iterator i = shards->begin(); i != shards->end(); i++ ) { string theShard = *i; ShardConnection conn( theShard , "" ); BSONObj res; conn->runCommand( dbName , cmdObj , res ); conn.done(); } return true; } } cmdShardingResetError; class CmdListDatabases : public Command { public: CmdListDatabases() : Command("listDatabases", true , "listdatabases" ) {} virtual bool logTheOp() { return false; } virtual bool slaveOk() const { return true; } virtual bool slaveOverrideOk() { return true; } virtual bool adminOnly() const { return true; } virtual LockType locktype() const { return NONE; } virtual void help( stringstream& help ) const { help << "list databases on cluster"; } bool run(const string& , BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) { vector shards; Shard::getAllShards( shards ); map sizes; map< string,shared_ptr > dbShardInfo; for ( vector::iterator i=shards.begin(); i!=shards.end(); i++ ) { Shard s = *i; BSONObj x = s.runCommand( "admin" , "listDatabases" ); BSONObjIterator j( x["databases"].Obj() ); while ( j.more() ) { BSONObj theDB = j.next().Obj(); string name = theDB["name"].String(); long long size = theDB["sizeOnDisk"].numberLong(); long long& totalSize = sizes[name]; if ( size == 1 ) { if ( totalSize <= 1 ) totalSize = 1; } else totalSize += size; shared_ptr& bb = dbShardInfo[name]; if ( ! bb.get() ) bb.reset( new BSONObjBuilder() ); bb->appendNumber( s.getName() , size ); } } long long totalSize = 0; BSONArrayBuilder bb( result.subarrayStart( "databases" ) ); for ( map::iterator i=sizes.begin(); i!=sizes.end(); ++i ) { string name = i->first; if ( name == "local" ) { // we don't return local // since all shards have their own independant local continue; } long long size = i->second; totalSize += size; BSONObjBuilder temp; temp.append( "name" , name ); temp.appendNumber( "sizeOnDisk" , size ); temp.appendBool( "empty" , size == 1 ); temp.append( "shards" , dbShardInfo[name]->obj() ); bb.append( temp.obj() ); } if ( sizes.find( "config" ) == sizes.end() ){ ScopedDbConnection conn( configServer.getPrimary() ); BSONObj x; if ( conn->simpleCommand( "config" , &x , "dbstats" ) ){ BSONObjBuilder b; b.append( "name" , "config" ); b.appendBool( "empty" , false ); if ( x["fileSize"].type() ) b.appendAs( x["fileSize"] , "sizeOnDisk" ); else b.append( "sizeOnDisk" , 1 ); bb.append( b.obj() ); } else { bb.append( BSON( "name" << "config" ) ); } conn.done(); } bb.done(); result.appendNumber( "totalSize" , totalSize ); result.appendNumber( "totalSizeMb" , totalSize / ( 1024 * 1024 ) ); return 1; } } cmdListDatabases; class CmdCloseAllDatabases : public Command { public: CmdCloseAllDatabases() : Command("closeAllDatabases", false , "closeAllDatabases" ) {} virtual bool logTheOp() { return false; } virtual bool slaveOk() const { return true; } virtual bool slaveOverrideOk() { return true; } virtual bool adminOnly() const { return true; } virtual LockType locktype() const { return NONE; } virtual void help( stringstream& help ) const { help << "Not supported sharded"; } bool run(const string& , BSONObj& jsobj, string& errmsg, BSONObjBuilder& /*result*/, bool /*fromRepl*/) { errmsg = "closeAllDatabases isn't supported through mongos"; return false; } } cmdCloseAllDatabases; class CmdReplSetGetStatus : public Command { public: CmdReplSetGetStatus() : Command("replSetGetStatus"){} virtual bool logTheOp() { return false; } virtual bool slaveOk() const { return true; } virtual bool adminOnly() const { return true; } virtual LockType locktype() const { return NONE; } virtual void help( stringstream& help ) const { help << "Not supported through mongos"; } bool run(const string& , BSONObj& jsobj, string& errmsg, BSONObjBuilder& /*result*/, bool /*fromRepl*/) { if ( jsobj["forShell"].trueValue() ) lastError.disableForCommand(); errmsg = "replSetGetStatus is not supported through mongos"; return false; } } cmdReplSetGetStatus; } // namespace mongo