summaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorAntonin Kral <a.kral@bobek.cz>2011-09-14 17:08:06 +0200
committerAntonin Kral <a.kral@bobek.cz>2011-09-14 17:08:06 +0200
commit5d342a758c6095b4d30aba0750b54f13b8916f51 (patch)
tree762e9aa84781f5e3b96db2c02d356c29cf0217c0 /shell
parentcbe2d992e9cd1ea66af9fa91df006106775d3073 (diff)
downloadmongodb-5d342a758c6095b4d30aba0750b54f13b8916f51.tar.gz
Imported Upstream version 2.0.0
Diffstat (limited to 'shell')
-rw-r--r--shell/collection.js52
-rw-r--r--shell/db.js77
-rw-r--r--shell/dbshell.cpp198
-rw-r--r--shell/mongo.js9
-rw-r--r--shell/mongo_vstudio.cpp491
-rw-r--r--shell/msvc/mongo.vcxproj14
-rw-r--r--shell/msvc/mongo.vcxproj.filters27
-rw-r--r--shell/query.js1
-rwxr-xr-x[-rw-r--r--]shell/servers.js1151
-rw-r--r--shell/shell_utils.cpp103
-rw-r--r--shell/utils.js284
-rw-r--r--shell/utils_sh.js98
12 files changed, 1941 insertions, 564 deletions
diff --git a/shell/collection.js b/shell/collection.js
index 8d4d4c7..1e6fe03 100644
--- a/shell/collection.js
+++ b/shell/collection.js
@@ -60,7 +60,7 @@ DBCollection.prototype.help = function () {
print("\tdb." + shortName + ".totalIndexSize() - size in bytes of all the indexes");
print("\tdb." + shortName + ".totalSize() - storage allocated for all data and indexes");
print("\tdb." + shortName + ".update(query, object[, upsert_bool, multi_bool])");
- print("\tdb." + shortName + ".validate() - SLOW");
+ print("\tdb." + shortName + ".validate( <full> ) - SLOW");;
print("\tdb." + shortName + ".getShardVersion() - only for use with sharding");
return __magicNoPrint;
}
@@ -120,7 +120,7 @@ DBCollection.prototype._validateObject = function( o ){
throw "can't save a DBQuery object";
}
-DBCollection._allowedFields = { $id : 1 , $ref : 1 };
+DBCollection._allowedFields = { $id : 1 , $ref : 1 , $db : 1 , $MinKey : 1, $MaxKey : 1 };
DBCollection.prototype._validateForStorage = function( o ){
this._validateObject( o );
@@ -374,21 +374,32 @@ DBCollection.prototype.renameCollection = function( newName , dropTarget ){
dropTarget : dropTarget } )
}
-DBCollection.prototype.validate = function() {
- var res = this._db.runCommand( { validate: this.getName() } );
+DBCollection.prototype.validate = function(full) {
+ var cmd = { validate: this.getName() };
- res.valid = false;
+ if (typeof(full) == 'object') // support arbitrary options here
+ Object.extend(cmd, full);
+ else
+ cmd.full = full;
+
+ var res = this._db.runCommand( cmd );
+
+ if (typeof(res.valid) == 'undefined') {
+ // old-style format just put everything in a string. Now using proper fields
- var raw = res.result || res.raw;
+ res.valid = false;
- if ( raw ){
- var str = "-" + tojson( raw );
- res.valid = ! ( str.match( /exception/ ) || str.match( /corrupt/ ) );
+ var raw = res.result || res.raw;
- var p = /lastExtentSize:(\d+)/;
- var r = p.exec( str );
- if ( r ){
- res.lastExtentSize = Number( r[1] );
+ if ( raw ){
+ var str = "-" + tojson( raw );
+ res.valid = ! ( str.match( /exception/ ) || str.match( /corrupt/ ) );
+
+ var p = /lastExtentSize:(\d+)/;
+ var r = p.exec( str );
+ if ( r ){
+ res.lastExtentSize = Number( r[1] );
+ }
}
}
@@ -530,13 +541,21 @@ DBCollection.prototype.isCapped = function(){
return ( e && e.options && e.options.capped ) ? true : false;
}
+DBCollection.prototype._distinct = function( keyString , query ){
+ return this._dbCommand( { distinct : this._shortName , key : keyString , query : query || {} } );
+ if ( ! res.ok )
+ throw "distinct failed: " + tojson( res );
+ return res.values;
+}
+
DBCollection.prototype.distinct = function( keyString , query ){
- var res = this._dbCommand( { distinct : this._shortName , key : keyString , query : query || {} } );
+ var res = this._distinct( keyString , query );
if ( ! res.ok )
throw "distinct failed: " + tojson( res );
return res.values;
}
+
DBCollection.prototype.group = function( params ){
params.ns = this._shortName;
return this._db.group( params );
@@ -578,7 +597,8 @@ MapReduceResult.prototype.drop = function(){
*/
MapReduceResult.prototype.convertToSingleObject = function(){
var z = {};
- this._coll.find().forEach( function(a){ z[a._id] = a.value; } );
+ var it = this.results != null ? this.results : this._coll.find();
+ it.forEach( function(a){ z[a._id] = a.value; } );
return z;
}
@@ -593,7 +613,7 @@ DBCollection.prototype.convertToSingleObject = function(valueField){
*/
DBCollection.prototype.mapReduce = function( map , reduce , optionsOrOutString ){
var c = { mapreduce : this._shortName , map : map , reduce : reduce };
- assert( optionsOrOutString , "need to an optionsOrOutString" )
+ assert( optionsOrOutString , "need to supply an optionsOrOutString" )
if ( typeof( optionsOrOutString ) == "string" )
c["out"] = optionsOrOutString;
diff --git a/shell/db.js b/shell/db.js
index 679f51e..2892359 100644
--- a/shell/db.js
+++ b/shell/db.js
@@ -22,8 +22,8 @@ DB.prototype.getName = function(){
return this._name;
}
-DB.prototype.stats = function(){
- return this.runCommand( { dbstats : 1 } );
+DB.prototype.stats = function(scale){
+ return this.runCommand( { dbstats : 1 , scale : scale } );
}
DB.prototype.getCollection = function( name ){
@@ -60,15 +60,26 @@ DB.prototype.adminCommand = function( obj ){
DB.prototype._adminCommand = DB.prototype.adminCommand; // alias old name
DB.prototype.addUser = function( username , pass, readOnly ){
+ if ( pass == null || pass.length == 0 )
+ throw "password can't be empty";
+
readOnly = readOnly || false;
var c = this.getCollection( "system.users" );
var u = c.findOne( { user : username } ) || { user : username };
u.readOnly = readOnly;
u.pwd = hex_md5( username + ":mongo:" + pass );
- print( tojson( u ) );
c.save( u );
+ var le = this.getLastErrorObj();
+ printjson( le )
+ if ( le.err )
+ throw "couldn't add user: " + le.err
+ print( tojson( u ) );
+}
+
+DB.prototype.logout = function(){
+ return this.runCommand({logout : 1});
}
DB.prototype.removeUser = function( username ){
@@ -124,6 +135,8 @@ DB.prototype.auth = function( username , pass ){
DB.prototype.createCollection = function(name, opt) {
var options = opt || {};
var cmd = { create: name, capped: options.capped, size: options.size, max: options.max };
+ if (options.autoIndexId != undefined)
+ cmd.autoIndexId = options.autoIndexId;
var res = this._dbCommand(cmd);
return res;
}
@@ -133,7 +146,7 @@ DB.prototype.createCollection = function(name, opt) {
* Returns the current profiling level of this database
* @return SOMETHING_FIXME or null on error
*/
-DB.prototype.getProfilingLevel = function() {
+DB.prototype.getProfilingLevel = function() {
var res = this._dbCommand( { profile: -1 } );
return res ? res.was : null;
}
@@ -143,7 +156,7 @@ DB.prototype.getProfilingLevel = function() {
* example { was : 0, slowms : 100 }
* @return SOMETHING_FIXME or null on error
*/
-DB.prototype.getProfilingStatus = function() {
+DB.prototype.getProfilingStatus = function() {
var res = this._dbCommand( { profile: -1 } );
if ( ! res.ok )
throw "profile command failed: " + tojson( res );
@@ -154,24 +167,37 @@ DB.prototype.getProfilingStatus = function() {
/**
Erase the entire database. (!)
-
+
* @return Object returned has member ok set to true if operation succeeds, false otherwise.
*/
-DB.prototype.dropDatabase = function() {
+DB.prototype.dropDatabase = function() {
if ( arguments.length )
throw "dropDatabase doesn't take arguments";
return this._dbCommand( { dropDatabase: 1 } );
}
-
-DB.prototype.shutdownServer = function() {
+/**
+ * Shuts down the database. Must be run while using the admin database.
+ * @param opts Options for shutdown. Possible options are:
+ * - force: (boolean) if the server should shut down, even if there is no
+ * up-to-date slave
+ * - timeoutSecs: (number) the server will continue checking over timeoutSecs
+ * if any other servers have caught up enough for it to shut down.
+ */
+DB.prototype.shutdownServer = function(opts) {
if( "admin" != this._name ){
return "shutdown command only works with the admin database; try 'use admin'";
}
+ cmd = {"shutdown" : 1};
+ opts = opts || {};
+ for (var o in opts) {
+ cmd[o] = opts[o];
+ }
+
try {
- var res = this._dbCommand("shutdown");
- if( res )
+ var res = this.runCommand(cmd);
+ if( res )
throw "shutdownServer failed: " + res.errmsg;
throw "shutdownServer failed";
}
@@ -298,6 +324,7 @@ DB.prototype.help = function() {
print("\tdb.isMaster() check replica primary status");
print("\tdb.killOp(opid) kills the current operation in the db");
print("\tdb.listCommands() lists all the db commands");
+ print("\tdb.logout()");
print("\tdb.printCollectionStats()");
print("\tdb.printReplicationInfo()");
print("\tdb.printSlaveReplicationInfo()");
@@ -312,6 +339,8 @@ DB.prototype.help = function() {
print("\tdb.stats()");
print("\tdb.version() current version of the server");
print("\tdb.getMongo().setSlaveOk() allow queries on a replication slave server");
+ print("\tdb.fsyncLock() flush data to disk and lock server for backups");
+ print("\tdb.fsyncUnock() unlocks server following a db.fsyncLock()");
return __magicNoPrint;
}
@@ -663,7 +692,12 @@ DB.prototype.getReplicationInfo = function() {
DB.prototype.printReplicationInfo = function() {
var result = this.getReplicationInfo();
- if( result.errmsg ) {
+ if( result.errmsg ) {
+ if (!this.isMaster().ismaster) {
+ print("this is a slave, printing slave replication info.");
+ this.printSlaveReplicationInfo();
+ return;
+ }
print(tojson(result));
return;
}
@@ -680,7 +714,7 @@ DB.prototype.printSlaveReplicationInfo = function() {
print("\t syncedTo: " + st.toString() );
var ago = (now-st)/1000;
var hrs = Math.round(ago/36)/100;
- print("\t\t = " + Math.round(ago) + "secs ago (" + hrs + "hrs)");
+ print("\t\t = " + Math.round(ago) + " secs ago (" + hrs + "hrs)");
};
function g(x) {
@@ -711,13 +745,14 @@ DB.prototype.printSlaveReplicationInfo = function() {
};
var L = this.getSiblingDB("local");
- if( L.sources.count() != 0 ) {
- L.sources.find().forEach(g);
- }
- else if (L.system.replset.count() != 0) {
+
+ if (L.system.replset.count() != 0) {
var status = this.adminCommand({'replSetGetStatus' : 1});
status.members.forEach(r);
}
+ else if( L.sources.count() != 0 ) {
+ L.sources.find().forEach(g);
+ }
else {
print("local.sources is empty; is this db a --slave?");
return;
@@ -773,6 +808,14 @@ DB.prototype.printShardingStatus = function( verbose ){
printShardingStatus( this.getSiblingDB( "config" ) , verbose );
}
+DB.prototype.fsyncLock = function() {
+ return db.adminCommand({fsync:1, lock:true});
+}
+
+DB.prototype.fsyncUnlock = function() {
+ return db.getSiblingDB("admin").$cmd.sys.unlock.findOne()
+}
+
DB.autocomplete = function(obj){
var colls = obj.getCollectionNames();
var ret=[];
diff --git a/shell/dbshell.cpp b/shell/dbshell.cpp
index 2e93682..f3122c7 100644
--- a/shell/dbshell.cpp
+++ b/shell/dbshell.cpp
@@ -17,19 +17,11 @@
#include "pch.h"
#include <stdio.h>
+#include <string.h>
-#if defined(_WIN32)
-# if defined(USE_READLINE)
-# define USE_READLINE_STATIC
-# endif
-#endif
-#ifdef USE_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#include <setjmp.h>
-jmp_buf jbuf;
-#endif
+#define USE_LINENOISE
+#include "../third_party/linenoise/linenoise.h"
#include "../scripting/engine.h"
#include "../client/dbclient.h"
@@ -52,10 +44,17 @@ static volatile bool atPrompt = false; // can eval before getting to prompt
bool autoKillOp = false;
-#if defined(USE_READLINE) && !defined(__freebsd__) && !defined(__openbsd__) && !defined(_WIN32)
-#define CTRLC_HANDLE
+#if defined(USE_LINENOISE) && !defined(__freebsd__) && !defined(__openbsd__) && !defined(_WIN32)
+// this is for ctrl-c handling
+#include <setjmp.h>
+jmp_buf jbuf;
+#endif
+
+#if defined(USE_LINENOISE)
+#define USE_TABCOMPLETION
#endif
+
namespace mongo {
Scope * shellMainScope;
@@ -67,7 +66,8 @@ void generateCompletions( const string& prefix , vector<string>& all ) {
if ( prefix.find( '"' ) != string::npos )
return;
- shellMainScope->invokeSafe("function(x) {shellAutocomplete(x)}", BSON("0" << prefix), 1000);
+ BSONObj args = BSON("0" << prefix);
+ shellMainScope->invokeSafe("function(x) {shellAutocomplete(x)}", &args, 0, 1000);
BSONObjBuilder b;
shellMainScope->append( b , "" , "__autocomplete__" );
BSONObj res = b.obj();
@@ -81,45 +81,19 @@ void generateCompletions( const string& prefix , vector<string>& all ) {
}
-#ifdef USE_READLINE
-static char** completionHook(const char* text , int start ,int end ) {
- static map<string,string> m;
-
+#ifdef USE_TABCOMPLETION
+void completionHook(const char* text , linenoiseCompletions* lc ) {
vector<string> all;
+ generateCompletions( text , all );
- generateCompletions( string(text,end) , all );
-
- if ( all.size() == 0 ) {
- return 0;
- }
-
- string longest = all[0];
- for ( vector<string>::iterator i=all.begin(); i!=all.end(); ++i ) {
- string s = *i;
- for ( unsigned j=0; j<s.size(); j++ ) {
- if ( longest[j] == s[j] )
- continue;
- longest = longest.substr(0,j);
- break;
- }
- }
-
- char ** matches = (char**)malloc( sizeof(char*) * (all.size()+2) );
- unsigned x=0;
- matches[x++] = strdup( longest.c_str() );
- for ( unsigned i=0; i<all.size(); i++ ) {
- matches[x++] = strdup( all[i].c_str() );
- }
- matches[x++] = 0;
-
- rl_completion_append_character = '\0'; // don't add a space after completions
+ for ( unsigned i=0; i<all.size(); i++ )
+ linenoiseAddCompletion( lc , (char*)all[i].c_str() );
- return matches;
}
#endif
void shellHistoryInit() {
-#ifdef USE_READLINE
+#ifdef USE_LINENOISE
stringstream ss;
char * h = getenv( "HOME" );
if ( h )
@@ -127,22 +101,22 @@ void shellHistoryInit() {
ss << ".dbshell";
historyFile = ss.str();
- using_history();
- read_history( historyFile.c_str() );
-
- rl_attempted_completion_function = completionHook;
+ linenoiseHistoryLoad( (char*)historyFile.c_str() );
+#ifdef USE_TABCOMPLETION
+ linenoiseSetCompletionCallback( completionHook );
+#endif
#else
//cout << "type \"exit\" to exit" << endl;
#endif
}
void shellHistoryDone() {
-#ifdef USE_READLINE
- write_history( historyFile.c_str() );
+#ifdef USE_LINENOISE
+ linenoiseHistorySave( (char*)historyFile.c_str() );
#endif
}
void shellHistoryAdd( const char * line ) {
-#ifdef USE_READLINE
+#ifdef USE_LINENOISE
if ( line[0] == '\0' )
return;
@@ -153,7 +127,7 @@ void shellHistoryAdd( const char * line ) {
lastLine = line;
if ((strstr(line, ".auth")) == NULL)
- add_history( line );
+ linenoiseHistoryAdd( line );
#endif
}
@@ -163,7 +137,6 @@ void intr( int sig ) {
#endif
}
-#if !defined(_WIN32)
void killOps() {
if ( mongo::shellUtils::_nokillop || mongo::shellUtils::_allMyUris.size() == 0 )
return;
@@ -208,32 +181,26 @@ void quitNicely( int sig ) {
gotInterrupted = 1;
return;
}
+
+#if !defined(_WIN32)
if ( sig == SIGPIPE )
mongo::rawOut( "mongo got signal SIGPIPE\n" );
+#endif
+
killOps();
shellHistoryDone();
exit(0);
}
-#else
-void quitNicely( int sig ) {
- mongo::dbexitCalled = true;
- //killOps();
- shellHistoryDone();
- exit(0);
-}
-#endif
char * shellReadline( const char * prompt , int handlesigint = 0 ) {
atPrompt = true;
-#ifdef USE_READLINE
-
- rl_bind_key('\t',rl_complete);
+#ifdef USE_LINENOISE
#ifdef CTRLC_HANDLE
if ( ! handlesigint ) {
- char* ret = readline( prompt );
+ char* ret = linenoise( prompt );
atPrompt = false;
return ret;
}
@@ -246,7 +213,7 @@ char * shellReadline( const char * prompt , int handlesigint = 0 ) {
signal( SIGINT , intr );
#endif
- char * ret = readline( prompt );
+ char * ret = linenoise( prompt );
signal( SIGINT , quitNicely );
atPrompt = false;
return ret;
@@ -262,8 +229,18 @@ char * shellReadline( const char * prompt , int handlesigint = 0 ) {
#endif
}
-#if !defined(_WIN32)
-#include <string.h>
+#ifdef _WIN32
+char * strsignal(int sig){
+ switch (sig){
+ case SIGINT: return "SIGINT";
+ case SIGTERM: return "SIGTERM";
+ case SIGABRT: return "SIGABRT";
+ case SIGSEGV: return "SIGSEGV";
+ case SIGFPE: return "SIGFPE";
+ default: return "unknown";
+ }
+}
+#endif
void quitAbruptly( int sig ) {
ostringstream ossSig;
@@ -289,16 +266,17 @@ void myterminate() {
void setupSignals() {
signal( SIGINT , quitNicely );
signal( SIGTERM , quitNicely );
- signal( SIGPIPE , quitNicely ); // Maybe just log and continue?
signal( SIGABRT , quitAbruptly );
signal( SIGSEGV , quitAbruptly );
- signal( SIGBUS , quitAbruptly );
signal( SIGFPE , quitAbruptly );
+
+#if !defined(_WIN32) // surprisingly these are the only ones that don't work on windows
+ signal( SIGPIPE , quitNicely ); // Maybe just log and continue?
+ signal( SIGBUS , quitAbruptly );
+#endif
+
set_terminate( myterminate );
}
-#else
-inline void setupSignals() {}
-#endif
string fixHost( string url , string host , string port ) {
//cout << "fixHost url: " << url << " host: " << host << " port: " << port << endl;
@@ -337,7 +315,7 @@ string fixHost( string url , string host , string port ) {
return newurl;
}
-static string OpSymbols = "~!%^&*-+=|:,<>/?";
+static string OpSymbols = "~!%^&*-+=|:,<>/?.";
bool isOpSymbol( char c ) {
for ( size_t i = 0; i < OpSymbols.size(); i++ )
@@ -410,6 +388,9 @@ public:
assert( ! isBalanced( "x = 5 +") );
assert( isBalanced( " x ++") );
assert( isBalanced( "-- x") );
+ assert( !isBalanced( "a.") );
+ assert( !isBalanced( "a. ") );
+ assert( isBalanced( "a.b") );
}
} balnaced_test;
@@ -422,6 +403,8 @@ string finishCode( string code ) {
return "";
if ( ! line )
return "";
+ if ( code.find("\n\n") != string::npos ) // cancel multiline if two blank lines are entered
+ return ";";
while (startsWith(line, "... "))
line += 4;
@@ -461,18 +444,6 @@ namespace mongo {
extern DBClientWithCommands *latestConn;
}
-string stateToString(MemberState s) {
- if( s.s == MemberState::RS_STARTUP ) return "STARTUP";
- if( s.s == MemberState::RS_PRIMARY ) return "PRIMARY";
- if( s.s == MemberState::RS_SECONDARY ) return "SECONDARY";
- if( s.s == MemberState::RS_RECOVERING ) return "RECOVERING";
- if( s.s == MemberState::RS_FATAL ) return "FATAL";
- if( s.s == MemberState::RS_STARTUP2 ) return "STARTUP2";
- if( s.s == MemberState::RS_ARBITER ) return "ARBITER";
- if( s.s == MemberState::RS_DOWN ) return "DOWN";
- if( s.s == MemberState::RS_ROLLBACK ) return "ROLLBACK";
- return "";
-}
string sayReplSetMemberState() {
try {
if( latestConn ) {
@@ -482,8 +453,10 @@ string sayReplSetMemberState() {
ss << info["set"].String() << ':';
int s = info["myState"].Int();
MemberState ms(s);
- ss << stateToString(ms);
- return ss.str();
+ return ms.toString();
+ }
+ else if( str::equals(info.getStringField("info"), "mongos") ) {
+ return "mongos";
}
}
}
@@ -509,6 +482,7 @@ int _main(int argc, char* argv[]) {
bool runShell = false;
bool nodb = false;
+ bool norc = false;
string script;
@@ -520,6 +494,7 @@ int _main(int argc, char* argv[]) {
shell_options.add_options()
("shell", "run the shell after executing files")
("nodb", "don't connect to mongod on startup - no 'db address' arg expected")
+ ("norc", "will not run the \".mongorc.js\" file on start up")
("quiet", "be less chatty" )
("port", po::value<string>(&port), "port to connect to")
("host", po::value<string>(&dbhost), "server to connect to")
@@ -531,6 +506,9 @@ int _main(int argc, char* argv[]) {
("version", "show version information")
("verbose", "increase verbosity")
("ipv6", "enable IPv6 support (disabled by default)")
+#ifdef MONGO_SSL
+ ("ssl", "use all for connections")
+#endif
;
hidden_options.add_options()
@@ -582,6 +560,9 @@ int _main(int argc, char* argv[]) {
if (params.count("nodb")) {
nodb = true;
}
+ if (params.count("norc")) {
+ norc = true;
+ }
if (params.count("help")) {
show_help_text(argv[0], shell_options);
return mongo::EXIT_CLEAN;
@@ -596,6 +577,11 @@ int _main(int argc, char* argv[]) {
if (params.count("quiet")) {
mongo::cmdLine.quiet = true;
}
+#ifdef MONGO_SSL
+ if (params.count("ssl")) {
+ mongo::cmdLine.sslOnNormalPorts = true;
+ }
+#endif
if (params.count("nokillop")) {
mongo::shellUtils::_nokillop = true;
}
@@ -603,6 +589,8 @@ int _main(int argc, char* argv[]) {
autoKillOp = true;
}
+
+
/* This is a bit confusing, here are the rules:
*
* if nodb is set then all positional parameters are files
@@ -696,8 +684,28 @@ int _main(int argc, char* argv[]) {
mongo::shellUtils::MongoProgramScope s;
+ if (!norc) {
+ string rcLocation;
+#ifndef _WIN32
+ if ( getenv("HOME") != NULL )
+ rcLocation = str::stream() << getenv("HOME") << "/.mongorc.js" ;
+#else
+ if ( getenv("HOMEDRIVE") != NULL && getenv("HOMEPATH") != NULL )
+ rcLocation = str::stream() << getenv("HOMEDRIVE") << getenv("HOMEPATH") << "\\.mongorc.js";
+#endif
+ if ( !rcLocation.empty() && fileExists(rcLocation) ) {
+ if ( ! scope->execFile( rcLocation , false , true , false , 0 ) ) {
+ cout << "The \".mongorc.js\" file located in your home folder could not be executed" << endl;
+ return -5;
+ }
+ }
+ }
+
shellHistoryInit();
+ string prompt;
+ int promptType;
+
//v8::Handle<v8::Object> shellHelper = baseContext_->Global()->Get( v8::String::New( "shellHelper" ) )->ToObject();
while ( 1 ) {
@@ -706,7 +714,15 @@ int _main(int argc, char* argv[]) {
// shellMainScope->localConnect;
//DBClientWithCommands *c = getConnection( JSContext *cx, JSObject *obj );
- string prompt(sayReplSetMemberState()+"> ");
+ promptType = scope->type("prompt");
+ if (promptType == String){
+ prompt = scope->getString("prompt");
+ } else if (promptType == Code) {
+ scope->exec("__prompt__ = prompt();", "", false, false, false, 0);
+ prompt = scope->getString("__prompt__");
+ } else {
+ prompt = sayReplSetMemberState()+"> ";
+ }
char * line = shellReadline( prompt.c_str() );
diff --git a/shell/mongo.js b/shell/mongo.js
index e129784..2535769 100644
--- a/shell/mongo.js
+++ b/shell/mongo.js
@@ -24,8 +24,9 @@ if ( typeof mongoInject == "function" ){
mongoInject( Mongo.prototype );
}
-Mongo.prototype.setSlaveOk = function() {
- this.slaveOk = true;
+Mongo.prototype.setSlaveOk = function( value ) {
+ if( value == undefined ) value = true
+ this.slaveOk = value
}
Mongo.prototype.getDB = function( name ){
@@ -43,6 +44,10 @@ Mongo.prototype.adminCommand = function( cmd ){
return this.getDB( "admin" ).runCommand( cmd );
}
+Mongo.prototype.setLogLevel = function( logLevel ){
+ return this.adminCommand({ setParameter : 1, logLevel : logLevel })
+}
+
Mongo.prototype.getDBNames = function(){
return this.getDBs().databases.map(
function(z){
diff --git a/shell/mongo_vstudio.cpp b/shell/mongo_vstudio.cpp
index c3c3751..5496ddb 100644
--- a/shell/mongo_vstudio.cpp
+++ b/shell/mongo_vstudio.cpp
@@ -15,7 +15,21 @@ const StringData _jscode_raw_utils =
"if ( a == b )\n"
"return true;\n"
"\n"
-"if ( tojson( a ) == tojson( b ) )\n"
+"a = tojson(a,false,true);\n"
+"b = tojson(b,false,true);\n"
+"\n"
+"if ( a == b )\n"
+"return true;\n"
+"\n"
+"var clean = function( s ){\n"
+"s = s.replace( /NumberInt\\((\\-?\\d+)\\)/g , \"$1\" );\n"
+"return s;\n"
+"}\n"
+"\n"
+"a = clean(a);\n"
+"b = clean(b);\n"
+"\n"
+"if ( a == b )\n"
"return true;\n"
"\n"
"return false;\n"
@@ -40,10 +54,8 @@ const StringData _jscode_raw_utils =
"\n"
"assert = function( b , msg ){\n"
"if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n"
-"\n"
"if ( b )\n"
"return;\n"
-"\n"
"doassert( msg == undefined ? \"assert failed\" : \"assert failed : \" + msg );\n"
"}\n"
"\n"
@@ -77,6 +89,26 @@ const StringData _jscode_raw_utils =
"doassert( \"[\" + a + \"] != [\" + b + \"] are equal : \" + msg );\n"
"}\n"
"\n"
+"assert.contains = function( o, arr, msg ){\n"
+"var wasIn = false\n"
+"\n"
+"if( ! arr.length ){\n"
+"for( i in arr ){\n"
+"wasIn = arr[i] == o || ( ( arr[i] != null && o != null ) && friendlyEqual( arr[i] , o ) )\n"
+"return;\n"
+"if( wasIn ) break\n"
+"}\n"
+"}\n"
+"else {\n"
+"for( var i = 0; i < arr.length; i++ ){\n"
+"wasIn = arr[i] == o || ( ( arr[i] != null && o != null ) && friendlyEqual( arr[i] , o ) )\n"
+"if( wasIn ) break\n"
+"}\n"
+"}\n"
+"\n"
+"if( ! wasIn ) doassert( tojson( o ) + \" was not in \" + tojson( arr ) + \" : \" + msg )\n"
+"}\n"
+"\n"
"assert.repeat = function( f, msg, timeout, interval ) {\n"
"if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n"
"\n"
@@ -204,6 +236,18 @@ const StringData _jscode_raw_utils =
"doassert( a + \" is not greater than or eq \" + b + \" : \" + msg );\n"
"}\n"
"\n"
+"assert.between = function( a, b, c, msg, inclusive ){\n"
+"if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n"
+"\n"
+"if( ( inclusive == undefined || inclusive == true ) &&\n"
+"a <= b && b <= c ) return;\n"
+"else if( a < b && b < c ) return;\n"
+"\n"
+"doassert( b + \" is not between \" + a + \" and \" + c + \" : \" + msg );\n"
+"}\n"
+"\n"
+"assert.betweenIn = function( a, b, c, msg ){ assert.between( a, b, c, msg, true ) }\n"
+"assert.betweenEx = function( a, b, c, msg ){ assert.between( a, b, c, msg, false ) }\n"
"\n"
"assert.close = function( a , b , msg , places ){\n"
"if (places === undefined) {\n"
@@ -231,6 +275,11 @@ const StringData _jscode_raw_utils =
"return dst;\n"
"}\n"
"\n"
+"Object.merge = function( dst, src, deep ){\n"
+"var clone = Object.extend( {}, dst, deep )\n"
+"return Object.extend( clone, src, deep )\n"
+"}\n"
+"\n"
"argumentsToArray = function( a ){\n"
"var arr = [];\n"
"for ( var i=0; i<a.length; i++ )\n"
@@ -375,20 +424,25 @@ const StringData _jscode_raw_utils =
"}\n"
"\n"
"\n"
-"Array.tojson = function( a , indent ){\n"
+"Array.tojson = function( a , indent , nolint ){\n"
+"var lineEnding = nolint ? \" \" : \"\\n\";\n"
+"\n"
"if (!indent)\n"
"indent = \"\";\n"
"\n"
+"if ( nolint )\n"
+"indent = \"\";\n"
+"\n"
"if (a.length == 0) {\n"
"return \"[ ]\";\n"
"}\n"
"\n"
-"var s = \"[\\n\";\n"
+"var s = \"[\" + lineEnding;\n"
"indent += \"\\t\";\n"
"for ( var i=0; i<a.length; i++){\n"
-"s += indent + tojson( a[i], indent );\n"
+"s += indent + tojson( a[i], indent , nolint );\n"
"if ( i < a.length - 1 ){\n"
-"s += \",\\n\";\n"
+"s += \",\" + lineEnding;\n"
"}\n"
"}\n"
"if ( a.length == 0 ) {\n"
@@ -396,7 +450,7 @@ const StringData _jscode_raw_utils =
"}\n"
"\n"
"indent = indent.substring(1);\n"
-"s += \"\\n\"+indent+\"]\";\n"
+"s += lineEnding+indent+\"]\";\n"
"return s;\n"
"}\n"
"\n"
@@ -464,6 +518,14 @@ const StringData _jscode_raw_utils =
"return this.toString();\n"
"}\n"
"\n"
+"if ( ! NumberInt.prototype ) {\n"
+"NumberInt.prototype = {}\n"
+"}\n"
+"\n"
+"NumberInt.prototype.tojson = function() {\n"
+"return this.toString();\n"
+"}\n"
+"\n"
"if ( ! ObjectId.prototype )\n"
"ObjectId.prototype = {}\n"
"\n"
@@ -538,16 +600,24 @@ const StringData _jscode_raw_utils =
"//return \"BinData type: \" + this.type + \" len: \" + this.len;\n"
"return this.toString();\n"
"}\n"
+"\n"
+"BinData.prototype.subtype = function () {\n"
+"return this.type;\n"
+"}\n"
+"\n"
+"BinData.prototype.length = function () {\n"
+"return this.len;\n"
+"}\n"
"}\n"
"else {\n"
"print( \"warning: no BinData class\" );\n"
"}\n"
"\n"
-"if ( typeof( UUID ) != \"undefined\" ){\n"
+"/*if ( typeof( UUID ) != \"undefined\" ){\n"
"UUID.prototype.tojson = function () {\n"
"return this.toString();\n"
"}\n"
-"}\n"
+"}*/\n"
"\n"
"if ( typeof _threadInject != \"undefined\" ){\n"
"print( \"fork() available!\" );\n"
@@ -672,7 +742,9 @@ const StringData _jscode_raw_utils =
"\"jstests/killop.js\",\n"
"\"jstests/run_program1.js\",\n"
"\"jstests/notablescan.js\",\n"
-"\"jstests/drop2.js\"] );\n"
+"\"jstests/drop2.js\",\n"
+"\"jstests/dropdb_race.js\",\n"
+"\"jstests/bench_test1.js\"] );\n"
"\n"
"// some tests can't be run in parallel with each other\n"
"var serialTestsArr = [ \"jstests/fsync.js\",\n"
@@ -908,6 +980,35 @@ const StringData _jscode_raw_utils =
"print( tojsononeline( x ) );\n"
"}\n"
"\n"
+"if ( typeof TestData == \"undefined\" ){\n"
+"TestData = undefined\n"
+"}\n"
+"\n"
+"jsTestName = function(){\n"
+"if( TestData ) return TestData.testName\n"
+"return \"__unknown_name__\"\n"
+"}\n"
+"\n"
+"jsTestFile = function(){\n"
+"if( TestData ) return TestData.testFile\n"
+"return \"__unknown_file__\"\n"
+"}\n"
+"\n"
+"jsTestPath = function(){\n"
+"if( TestData ) return TestData.testPath\n"
+"return \"__unknown_path__\"\n"
+"}\n"
+"\n"
+"jsTestOptions = function(){\n"
+"if( TestData ) return { noJournal : TestData.noJournal,\n"
+"noJournalPrealloc : TestData.noJournalPrealloc }\n"
+"return {}\n"
+"}\n"
+"\n"
+"testLog = function(x){\n"
+"print( jsTestFile() + \" - \" + x )\n"
+"}\n"
+"\n"
"shellPrintHelper = function (x) {\n"
"\n"
"if (typeof (x) == \"undefined\") {\n"
@@ -961,7 +1062,6 @@ const StringData _jscode_raw_utils =
"\n"
"builtinMethods[Mongo] = \"find update insert remove\".split(' ');\n"
"builtinMethods[BinData] = \"hex base64 length subtype\".split(' ');\n"
-"builtinMethods[NumberLong] = \"toNumber\".split(' ');\n"
"\n"
"var extraGlobals = \"Infinity NaN undefined null true false decodeURI decodeURIComponent encodeURI encodeURIComponent escape eval isFinite isNaN parseFloat parseInt unescape Array Boolean Date Math Number RegExp String print load gc MinKey MaxKey Mongo NumberLong ObjectId DBPointer UUID BinData Map\".split(' ');\n"
"\n"
@@ -1022,7 +1122,7 @@ const StringData _jscode_raw_utils =
"var p = possibilities[i];\n"
"if (typeof(curObj[p]) == \"undefined\" && curObj != global) continue; // extraGlobals aren't in the global object\n"
"if (p.length == 0 || p.length < lastPrefix.length) continue;\n"
-"if (isPrivate(p)) continue;\n"
+"if (lastPrefix[0] != '_' && isPrivate(p)) continue;\n"
"if (p.match(/^[0-9]+$/)) continue; // don't array number indexes\n"
"if (p.substr(0, lastPrefix.length) != lastPrefix) continue;\n"
"\n"
@@ -1051,7 +1151,7 @@ const StringData _jscode_raw_utils =
"\n"
"shellHelper = function( command , rest , shouldPrint ){\n"
"command = command.trim();\n"
-"var args = rest.trim().replace(/;$/,\"\").split( \"\\s+\" );\n"
+"var args = rest.trim().replace(/\\s*;$/,\"\").split( \"\\s+\" );\n"
"\n"
"if ( ! shellHelper[command] )\n"
"throw \"no command [\" + command + \"]\";\n"
@@ -1084,6 +1184,10 @@ const StringData _jscode_raw_utils =
"shellHelper.show = function (what) {\n"
"assert(typeof what == \"string\");\n"
"\n"
+"var args = what.split( /\\s+/ );\n"
+"what = args[0]\n"
+"args = args.splice(1)\n"
+"\n"
"if (what == \"profile\") {\n"
"if (db.system.profile.count() == 0) {\n"
"print(\"db.system.profile is empty\");\n"
@@ -1092,7 +1196,32 @@ const StringData _jscode_raw_utils =
"}\n"
"else {\n"
"print();\n"
-"db.system.profile.find({ millis: { $gt: 0} }).sort({ $natural: -1 }).limit(5).forEach(function (x) { print(\"\" + x.millis + \"ms \" + String(x.ts).substring(0, 24)); print(x.info); print(\"\\n\"); })\n"
+"db.system.profile.find({ millis: { $gt: 0} }).sort({ $natural: -1 }).limit(5).forEach(\n"
+"function (x) {\n"
+"print(\"\" + x.op + \"\\t\" + x.ns + \" \" + x.millis + \"ms \" + String(x.ts).substring(0, 24));\n"
+"var l = \"\";\n"
+"for ( var z in x ){\n"
+"if ( z == \"op\" || z == \"ns\" || z == \"millis\" || z == \"ts\" )\n"
+"continue;\n"
+"\n"
+"var val = x[z];\n"
+"var mytype = typeof(val);\n"
+"\n"
+"if ( mytype == \"string\" ||\n"
+"mytype == \"number\" )\n"
+"l += z + \":\" + val + \" \";\n"
+"else if ( mytype == \"object\" )\n"
+"l += z + \":\" + tojson(val ) + \" \";\n"
+"else if ( mytype == \"boolean\" )\n"
+"l += z + \" \";\n"
+"else\n"
+"l += z + \":\" + val + \" \";\n"
+"\n"
+"}\n"
+"print( l );\n"
+"print(\"\\n\");\n"
+"}\n"
+")\n"
"}\n"
"return \"\";\n"
"}\n"
@@ -1123,6 +1252,27 @@ const StringData _jscode_raw_utils =
"return \"\";\n"
"}\n"
"\n"
+"if (what == \"log\" ) {\n"
+"var n = \"global\";\n"
+"if ( args.length > 0 )\n"
+"n = args[0]\n"
+"\n"
+"var res = db.adminCommand( { getLog : n } )\n"
+"for ( var i=0; i<res.log.length; i++){\n"
+"print( res.log[i] )\n"
+"}\n"
+"return \"\"\n"
+"}\n"
+"\n"
+"if (what == \"logs\" ) {\n"
+"var res = db.adminCommand( { getLog : \"*\" } )\n"
+"for ( var i=0; i<res.names.length; i++){\n"
+"print( res.names[i] )\n"
+"}\n"
+"return \"\"\n"
+"}\n"
+"\n"
+"\n"
"throw \"don't know how to show [\" + what + \"]\";\n"
"\n"
"}\n"
@@ -1319,18 +1469,40 @@ const StringData _jscode_raw_utils =
"rs.status = function () { return db._adminCommand(\"replSetGetStatus\"); }\n"
"rs.isMaster = function () { return db.isMaster(); }\n"
"rs.initiate = function (c) { return db._adminCommand({ replSetInitiate: c }); }\n"
-"rs.reconfig = function (cfg) {\n"
-"cfg.version = rs.conf().version + 1;\n"
+"rs._runCmd = function (c) {\n"
+"// after the command, catch the disconnect and reconnect if necessary\n"
"var res = null;\n"
"try {\n"
-"res = db.adminCommand({ replSetReconfig: cfg });\n"
+"res = db.adminCommand(c);\n"
"}\n"
"catch (e) {\n"
-"print(\"shell got exception during reconfig: \" + e);\n"
+"if ((\"\" + e).indexOf(\"error doing query\") >= 0) {\n"
+"// closed connection. reconnect.\n"
+"db.getLastErrorObj();\n"
+"var o = db.getLastErrorObj();\n"
+"if (o.ok) {\n"
+"print(\"reconnected to server after rs command (which is normal)\");\n"
+"}\n"
+"else {\n"
+"printjson(o);\n"
+"}\n"
+"}\n"
+"else {\n"
+"print(\"shell got exception during repl set operation: \" + e);\n"
"print(\"in some circumstances, the primary steps down and closes connections on a reconfig\");\n"
"}\n"
+"return \"\";\n"
+"}\n"
"return res;\n"
"}\n"
+"rs.reconfig = function (cfg, options) {\n"
+"cfg.version = rs.conf().version + 1;\n"
+"cmd = { replSetReconfig: cfg };\n"
+"for (var i in options) {\n"
+"cmd[i] = options[i];\n"
+"}\n"
+"return this._runCmd(cmd);\n"
+"}\n"
"rs.add = function (hostport, arb) {\n"
"var cfg = hostport;\n"
"\n"
@@ -1350,20 +1522,13 @@ const StringData _jscode_raw_utils =
"cfg.arbiterOnly = true;\n"
"}\n"
"c.members.push(cfg);\n"
-"var res = null;\n"
-"try {\n"
-"res = db.adminCommand({ replSetReconfig: c });\n"
-"}\n"
-"catch (e) {\n"
-"print(\"shell got exception during reconfig: \" + e);\n"
-"print(\"in some circumstances, the primary steps down and closes connections on a reconfig\");\n"
+"return this._runCmd({ replSetReconfig: c });\n"
"}\n"
-"return res;\n"
-"}\n"
-"rs.stepDown = function (secs) { return db._adminCommand({ replSetStepDown:secs||60}); }\n"
+"rs.stepDown = function (secs) { return db._adminCommand({ replSetStepDown:(secs === undefined) ? 60:secs}); }\n"
"rs.freeze = function (secs) { return db._adminCommand({replSetFreeze:secs}); }\n"
"rs.addArb = function (hn) { return this.add(hn, true); }\n"
"rs.conf = function () { return db.getSisterDB(\"local\").system.replset.findOne(); }\n"
+"rs.config = function () { return rs.conf(); }\n"
"\n"
"rs.remove = function (hn) {\n"
"var local = db.getSisterDB(\"local\");\n"
@@ -1382,6 +1547,41 @@ const StringData _jscode_raw_utils =
"return \"error: couldn't find \"+hn+\" in \"+tojson(c.members);\n"
"};\n"
"\n"
+"rs.debug = {};\n"
+"\n"
+"rs.debug.nullLastOpWritten = function(primary, secondary) {\n"
+"var p = connect(primary+\"/local\");\n"
+"var s = connect(secondary+\"/local\");\n"
+"s.getMongo().setSlaveOk();\n"
+"\n"
+"var secondToLast = s.oplog.rs.find().sort({$natural : -1}).limit(1).next();\n"
+"var last = p.runCommand({findAndModify : \"oplog.rs\",\n"
+"query : {ts : {$gt : secondToLast.ts}},\n"
+"sort : {$natural : 1},\n"
+"update : {$set : {op : \"n\"}}});\n"
+"\n"
+"if (!last.value.o || !last.value.o._id) {\n"
+"print(\"couldn't find an _id?\");\n"
+"}\n"
+"else {\n"
+"last.value.o = {_id : last.value.o._id};\n"
+"}\n"
+"\n"
+"print(\"nulling out this op:\");\n"
+"printjson(last);\n"
+"};\n"
+"\n"
+"rs.debug.getLastOpWritten = function(server) {\n"
+"var s = db.getSisterDB(\"local\");\n"
+"if (server) {\n"
+"s = connect(server+\"/local\");\n"
+"}\n"
+"s.getMongo().setSlaveOk();\n"
+"\n"
+"return s.oplog.rs.find().sort({$natural : -1}).limit(1).next();\n"
+"};\n"
+"\n"
+"\n"
"help = shellHelper.help = function (x) {\n"
"if (x == \"mr\") {\n"
"print(\"\\nSee also http://www.mongodb.org/display/DOCS/MapReduce\");\n"
@@ -1413,6 +1613,17 @@ const StringData _jscode_raw_utils =
"print(\"\\nNote: the REPL prompt only auto-reports getLastError() for the shell command line connection.\\n\");\n"
"return;\n"
"}\n"
+"else if (x == \"keys\") {\n"
+"print(\"Tab completion and command history is available at the command prompt.\\n\");\n"
+"print(\"Some emacs keystrokes are available too:\");\n"
+"print(\" Ctrl-A start of line\");\n"
+"print(\" Ctrl-E end of line\");\n"
+"print(\" Ctrl-K del to end of line\");\n"
+"print(\"\\nMulti-line commands\");\n"
+"print(\"You can enter a multi line javascript expression. If parens, braces, etc. are not closed, you will see a new line \");\n"
+"print(\"beginning with '...' characters. Type the rest of your expression. Press Ctrl-C to abort the data entry if you\");\n"
+"print(\"get stuck.\\n\");\n"
+"}\n"
"else if (x == \"misc\") {\n"
"print(\"\\tb = new BinData(subtype,base64str) create a BSON BinData value\");\n"
"print(\"\\tb.subtype() the BinData subtype (0..255)\");\n"
@@ -1421,6 +1632,10 @@ const StringData _jscode_raw_utils =
"print(\"\\tb.base64() the data as a base 64 encoded string\");\n"
"print(\"\\tb.toString()\");\n"
"print();\n"
+"print(\"\\tb = HexData(subtype,hexstr) create a BSON BinData value from a hex string\");\n"
+"print(\"\\tb = UUID(hexstr) create a BSON BinData value of UUID subtype\");\n"
+"print(\"\\tb = MD5(hexstr) create a BSON BinData value of MD5 subtype\");\n"
+"print();\n"
"print(\"\\to = new ObjectId() create a new ObjectId\");\n"
"print(\"\\to.getTimestamp() return timestamp derived from first 32 bits of the OID\");\n"
"print(\"\\to.isObjectId()\");\n"
@@ -1459,15 +1674,18 @@ const StringData _jscode_raw_utils =
"print(\"\\t\" + \"db.help() help on db methods\");\n"
"print(\"\\t\" + \"db.mycoll.help() help on collection methods\");\n"
"print(\"\\t\" + \"rs.help() help on replica set methods\");\n"
-"print(\"\\t\" + \"help connect connecting to a db help\");\n"
"print(\"\\t\" + \"help admin administrative help\");\n"
+"print(\"\\t\" + \"help connect connecting to a db help\");\n"
+"print(\"\\t\" + \"help keys key shortcuts\");\n"
"print(\"\\t\" + \"help misc misc things to know\");\n"
-"print(\"\\t\" + \"help mr mapreduce help\");\n"
+"print(\"\\t\" + \"help mr mapreduce\");\n"
"print();\n"
"print(\"\\t\" + \"show dbs show database names\");\n"
"print(\"\\t\" + \"show collections show collections in current database\");\n"
"print(\"\\t\" + \"show users show users in current database\");\n"
"print(\"\\t\" + \"show profile show most recent system.profile entries with time >= 1ms\");\n"
+"print(\"\\t\" + \"show logs show the accessible logger names\");\n"
+"print(\"\\t\" + \"show log [name] prints out the last segment of log in memory, 'global' is default\");\n"
"print(\"\\t\" + \"use <db_name> set current database\");\n"
"print(\"\\t\" + \"db.foo.find() list objects in collection foo\");\n"
"print(\"\\t\" + \"db.foo.find( { a : 1 } ) list objects in foo where a == 1\");\n"
@@ -1481,6 +1699,108 @@ const StringData _jscode_raw_utils =
;
extern const JSFile utils;
const JSFile utils = { "shell/utils.js" , _jscode_raw_utils };
+const StringData _jscode_raw_utils_sh =
+"sh = function() { return \"try sh.help();\" }\n"
+"\n"
+"\n"
+"sh._checkMongos = function() {\n"
+"var x = db.runCommand( \"ismaster\" );\n"
+"if ( x.msg != \"isdbgrid\" )\n"
+"throw \"not connected to a mongos\"\n"
+"}\n"
+"\n"
+"sh._checkFullName = function( fullName ) {\n"
+"assert( fullName , \"neeed a full name\" )\n"
+"assert( fullName.indexOf( \".\" ) > 0 , \"name needs to be fully qualified <db>.<collection>'\" )\n"
+"}\n"
+"\n"
+"sh._adminCommand = function( cmd , skipCheck ) {\n"
+"if ( ! skipCheck ) sh._checkMongos();\n"
+"var res = db.getSisterDB( \"admin\" ).runCommand( cmd );\n"
+"\n"
+"if ( res == null || ! res.ok ) {\n"
+"print( \"command failed: \" + tojson( res ) )\n"
+"}\n"
+"\n"
+"return res;\n"
+"}\n"
+"\n"
+"sh.help = function() {\n"
+"print( \"\\tsh.addShard( host ) server:port OR setname/server:port\" )\n"
+"print( \"\\tsh.enableSharding(dbname) enables sharding on the database dbname\" )\n"
+"print( \"\\tsh.shardCollection(fullName,key,unique) shards the collection\" );\n"
+"\n"
+"print( \"\\tsh.splitFind(fullName,find) splits the chunk that find is in at the median\" );\n"
+"print( \"\\tsh.splitAt(fullName,middle) splits the chunk that middle is in at middle\" );\n"
+"print( \"\\tsh.moveChunk(fullName,find,to) move the chunk where 'find' is to 'to' (name of shard)\");\n"
+"\n"
+"print( \"\\tsh.setBalancerState( <bool on or not> ) turns the balancer on or off true=on, false=off\" );\n"
+"print( \"\\tsh.getBalancerState() return true if on, off if not\" );\n"
+"print( \"\\tsh.isBalancerRunning() return true if the balancer is running on any mongos\" );\n"
+"\n"
+"print( \"\\tsh.status() prints a general overview of the cluster\" )\n"
+"}\n"
+"\n"
+"sh.status = function( verbose , configDB ) {\n"
+"// TODO: move the actual commadn here\n"
+"printShardingStatus( configDB , verbose );\n"
+"}\n"
+"\n"
+"sh.addShard = function( url ){\n"
+"sh._adminCommand( { addShard : url } , true )\n"
+"}\n"
+"\n"
+"sh.enableSharding = function( dbname ) {\n"
+"assert( dbname , \"need a valid dbname\" )\n"
+"sh._adminCommand( { enableSharding : dbname } )\n"
+"}\n"
+"\n"
+"sh.shardCollection = function( fullName , key , unique ) {\n"
+"sh._checkFullName( fullName )\n"
+"assert( key , \"need a key\" )\n"
+"assert( typeof( key ) == \"object\" , \"key needs to be an object\" )\n"
+"\n"
+"var cmd = { shardCollection : fullName , key : key }\n"
+"if ( unique )\n"
+"cmd.unique = true;\n"
+"\n"
+"sh._adminCommand( cmd )\n"
+"}\n"
+"\n"
+"\n"
+"sh.splitFind = function( fullName , find ) {\n"
+"sh._checkFullName( fullName )\n"
+"sh._adminCommand( { split : fullName , find : find } )\n"
+"}\n"
+"\n"
+"sh.splitAt = function( fullName , middle ) {\n"
+"sh._checkFullName( fullName )\n"
+"sh._adminCommand( { split : fullName , middle : middle } )\n"
+"}\n"
+"\n"
+"sh.moveChunk = function( fullName , find , to ) {\n"
+"sh._checkFullName( fullName );\n"
+"sh._adminCommand( { moveChunk : fullName , find : find , to : to } )\n"
+"}\n"
+"\n"
+"sh.setBalancerState = function( onOrNot ) {\n"
+"db.getSisterDB( \"config\" ).settings.update({ _id: \"balancer\" }, { $set : { stopped: onOrNot ? false : true } }, true );\n"
+"}\n"
+"\n"
+"sh.getBalancerState = function() {\n"
+"var x = db.getSisterDB( \"config\" ).settings.findOne({ _id: \"balancer\" } )\n"
+"if ( x == null )\n"
+"return true;\n"
+"return ! x.stopped;\n"
+"}\n"
+"\n"
+"sh.isBalancerRunning = function() {\n"
+"var x = db.getSisterDB( \"config\" ).locks.findOne( { _id : \"balancer\" } );\n"
+"return x.state > 0;\n"
+"}\n"
+;
+extern const JSFile utils_sh;
+const JSFile utils_sh = { "shell/utils_sh.js" , _jscode_raw_utils_sh };
const StringData _jscode_raw_db =
"// db.js\n"
"\n"
@@ -1506,8 +1826,8 @@ const StringData _jscode_raw_db =
"return this._name;\n"
"}\n"
"\n"
-"DB.prototype.stats = function(){\n"
-"return this.runCommand( { dbstats : 1 } );\n"
+"DB.prototype.stats = function(scale){\n"
+"return this.runCommand( { dbstats : 1 , scale : scale } );\n"
"}\n"
"\n"
"DB.prototype.getCollection = function( name ){\n"
@@ -1544,15 +1864,26 @@ const StringData _jscode_raw_db =
"DB.prototype._adminCommand = DB.prototype.adminCommand; // alias old name\n"
"\n"
"DB.prototype.addUser = function( username , pass, readOnly ){\n"
+"if ( pass == null || pass.length == 0 )\n"
+"throw \"password can't be empty\";\n"
+"\n"
"readOnly = readOnly || false;\n"
"var c = this.getCollection( \"system.users\" );\n"
"\n"
"var u = c.findOne( { user : username } ) || { user : username };\n"
"u.readOnly = readOnly;\n"
"u.pwd = hex_md5( username + \":mongo:\" + pass );\n"
-"print( tojson( u ) );\n"
"\n"
"c.save( u );\n"
+"var le = this.getLastErrorObj();\n"
+"printjson( le )\n"
+"if ( le.err )\n"
+"throw \"couldn't add user: \" + le.err\n"
+"print( tojson( u ) );\n"
+"}\n"
+"\n"
+"DB.prototype.logout = function(){\n"
+"return this.runCommand({logout : 1});\n"
"}\n"
"\n"
"DB.prototype.removeUser = function( username ){\n"
@@ -1608,6 +1939,8 @@ const StringData _jscode_raw_db =
"DB.prototype.createCollection = function(name, opt) {\n"
"var options = opt || {};\n"
"var cmd = { create: name, capped: options.capped, size: options.size, max: options.max };\n"
+"if (options.autoIndexId != undefined)\n"
+"cmd.autoIndexId = options.autoIndexId;\n"
"var res = this._dbCommand(cmd);\n"
"return res;\n"
"}\n"
@@ -1647,14 +1980,27 @@ const StringData _jscode_raw_db =
"return this._dbCommand( { dropDatabase: 1 } );\n"
"}\n"
"\n"
-"\n"
-"DB.prototype.shutdownServer = function() {\n"
+"/**\n"
+"* Shuts down the database. Must be run while using the admin database.\n"
+"* @param opts Options for shutdown. Possible options are:\n"
+"* - force: (boolean) if the server should shut down, even if there is no\n"
+"* up-to-date slave\n"
+"* - timeoutSecs: (number) the server will continue checking over timeoutSecs\n"
+"* if any other servers have caught up enough for it to shut down.\n"
+"*/\n"
+"DB.prototype.shutdownServer = function(opts) {\n"
"if( \"admin\" != this._name ){\n"
"return \"shutdown command only works with the admin database; try 'use admin'\";\n"
"}\n"
"\n"
+"cmd = {\"shutdown\" : 1};\n"
+"opts = opts || {};\n"
+"for (var o in opts) {\n"
+"cmd[o] = opts[o];\n"
+"}\n"
+"\n"
"try {\n"
-"var res = this._dbCommand(\"shutdown\");\n"
+"var res = this.runCommand(cmd);\n"
"if( res )\n"
"throw \"shutdownServer failed: \" + res.errmsg;\n"
"throw \"shutdownServer failed\";\n"
@@ -1782,6 +2128,7 @@ const StringData _jscode_raw_db =
"print(\"\\tdb.isMaster() check replica primary status\");\n"
"print(\"\\tdb.killOp(opid) kills the current operation in the db\");\n"
"print(\"\\tdb.listCommands() lists all the db commands\");\n"
+"print(\"\\tdb.logout()\");\n"
"print(\"\\tdb.printCollectionStats()\");\n"
"print(\"\\tdb.printReplicationInfo()\");\n"
"print(\"\\tdb.printSlaveReplicationInfo()\");\n"
@@ -1796,6 +2143,8 @@ const StringData _jscode_raw_db =
"print(\"\\tdb.stats()\");\n"
"print(\"\\tdb.version() current version of the server\");\n"
"print(\"\\tdb.getMongo().setSlaveOk() allow queries on a replication slave server\");\n"
+"print(\"\\tdb.fsyncLock() flush data to disk and lock server for backups\");\n"
+"print(\"\\tdb.fsyncUnock() unlocks server following a db.fsyncLock()\");\n"
"\n"
"return __magicNoPrint;\n"
"}\n"
@@ -2148,6 +2497,11 @@ const StringData _jscode_raw_db =
"DB.prototype.printReplicationInfo = function() {\n"
"var result = this.getReplicationInfo();\n"
"if( result.errmsg ) {\n"
+"if (!this.isMaster().ismaster) {\n"
+"print(\"this is a slave, printing slave replication info.\");\n"
+"this.printSlaveReplicationInfo();\n"
+"return;\n"
+"}\n"
"print(tojson(result));\n"
"return;\n"
"}\n"
@@ -2164,7 +2518,7 @@ const StringData _jscode_raw_db =
"print(\"\\t syncedTo: \" + st.toString() );\n"
"var ago = (now-st)/1000;\n"
"var hrs = Math.round(ago/36)/100;\n"
-"print(\"\\t\\t = \" + Math.round(ago) + \"secs ago (\" + hrs + \"hrs)\");\n"
+"print(\"\\t\\t = \" + Math.round(ago) + \" secs ago (\" + hrs + \"hrs)\");\n"
"};\n"
"\n"
"function g(x) {\n"
@@ -2195,13 +2549,14 @@ const StringData _jscode_raw_db =
"};\n"
"\n"
"var L = this.getSiblingDB(\"local\");\n"
-"if( L.sources.count() != 0 ) {\n"
-"L.sources.find().forEach(g);\n"
-"}\n"
-"else if (L.system.replset.count() != 0) {\n"
+"\n"
+"if (L.system.replset.count() != 0) {\n"
"var status = this.adminCommand({'replSetGetStatus' : 1});\n"
"status.members.forEach(r);\n"
"}\n"
+"else if( L.sources.count() != 0 ) {\n"
+"L.sources.find().forEach(g);\n"
+"}\n"
"else {\n"
"print(\"local.sources is empty; is this db a --slave?\");\n"
"return;\n"
@@ -2257,6 +2612,14 @@ const StringData _jscode_raw_db =
"printShardingStatus( this.getSiblingDB( \"config\" ) , verbose );\n"
"}\n"
"\n"
+"DB.prototype.fsyncLock = function() {\n"
+"return db.adminCommand({fsync:1, lock:true});\n"
+"}\n"
+"\n"
+"DB.prototype.fsyncUnlock = function() {\n"
+"return db.getSiblingDB(\"admin\").$cmd.sys.unlock.findOne()\n"
+"}\n"
+"\n"
"DB.autocomplete = function(obj){\n"
"var colls = obj.getCollectionNames();\n"
"var ret=[];\n"
@@ -2296,8 +2659,9 @@ const StringData _jscode_raw_mongo =
"mongoInject( Mongo.prototype );\n"
"}\n"
"\n"
-"Mongo.prototype.setSlaveOk = function() {\n"
-"this.slaveOk = true;\n"
+"Mongo.prototype.setSlaveOk = function( value ) {\n"
+"if( value == undefined ) value = true\n"
+"this.slaveOk = value\n"
"}\n"
"\n"
"Mongo.prototype.getDB = function( name ){\n"
@@ -2315,6 +2679,10 @@ const StringData _jscode_raw_mongo =
"return this.getDB( \"admin\" ).runCommand( cmd );\n"
"}\n"
"\n"
+"Mongo.prototype.setLogLevel = function( logLevel ){\n"
+"return this.adminCommand({ setParameter : 1, logLevel : logLevel })\n"
+"}\n"
+"\n"
"Mongo.prototype.getDBNames = function(){\n"
"return this.getDBs().databases.map(\n"
"function(z){\n"
@@ -2500,7 +2868,6 @@ const StringData _jscode_raw_query =
"print(\"\\t.showDiskLoc() - adds a $diskLoc field to each returned object\")\n"
"print(\"\\nCursor methods\");\n"
"print(\"\\t.forEach( func )\")\n"
-"print(\"\\t.print() - output to console in full pretty format\")\n"
"print(\"\\t.map( func )\")\n"
"print(\"\\t.hasNext()\")\n"
"print(\"\\t.next()\")\n"
@@ -2847,7 +3214,7 @@ const StringData _jscode_raw_collection =
"print(\"\\tdb.\" + shortName + \".totalIndexSize() - size in bytes of all the indexes\");\n"
"print(\"\\tdb.\" + shortName + \".totalSize() - storage allocated for all data and indexes\");\n"
"print(\"\\tdb.\" + shortName + \".update(query, object[, upsert_bool, multi_bool])\");\n"
-"print(\"\\tdb.\" + shortName + \".validate() - SLOW\");\n"
+"print(\"\\tdb.\" + shortName + \".validate( <full> ) - SLOW\");;\n"
"print(\"\\tdb.\" + shortName + \".getShardVersion() - only for use with sharding\");\n"
"return __magicNoPrint;\n"
"}\n"
@@ -2907,7 +3274,7 @@ const StringData _jscode_raw_collection =
"throw \"can't save a DBQuery object\";\n"
"}\n"
"\n"
-"DBCollection._allowedFields = { $id : 1 , $ref : 1 };\n"
+"DBCollection._allowedFields = { $id : 1 , $ref : 1 , $db : 1 , $MinKey : 1, $MaxKey : 1 };\n"
"\n"
"DBCollection.prototype._validateForStorage = function( o ){\n"
"this._validateObject( o );\n"
@@ -3161,8 +3528,18 @@ const StringData _jscode_raw_collection =
"dropTarget : dropTarget } )\n"
"}\n"
"\n"
-"DBCollection.prototype.validate = function() {\n"
-"var res = this._db.runCommand( { validate: this.getName() } );\n"
+"DBCollection.prototype.validate = function(full) {\n"
+"var cmd = { validate: this.getName() };\n"
+"\n"
+"if (typeof(full) == 'object') // support arbitrary options here\n"
+"Object.extend(cmd, full);\n"
+"else\n"
+"cmd.full = full;\n"
+"\n"
+"var res = this._db.runCommand( cmd );\n"
+"\n"
+"if (typeof(res.valid) == 'undefined') {\n"
+"// old-style format just put everything in a string. Now using proper fields\n"
"\n"
"res.valid = false;\n"
"\n"
@@ -3178,6 +3555,7 @@ const StringData _jscode_raw_collection =
"res.lastExtentSize = Number( r[1] );\n"
"}\n"
"}\n"
+"}\n"
"\n"
"return res;\n"
"}\n"
@@ -3317,13 +3695,21 @@ const StringData _jscode_raw_collection =
"return ( e && e.options && e.options.capped ) ? true : false;\n"
"}\n"
"\n"
+"DBCollection.prototype._distinct = function( keyString , query ){\n"
+"return this._dbCommand( { distinct : this._shortName , key : keyString , query : query || {} } );\n"
+"if ( ! res.ok )\n"
+"throw \"distinct failed: \" + tojson( res );\n"
+"return res.values;\n"
+"}\n"
+"\n"
"DBCollection.prototype.distinct = function( keyString , query ){\n"
-"var res = this._dbCommand( { distinct : this._shortName , key : keyString , query : query || {} } );\n"
+"var res = this._distinct( keyString , query );\n"
"if ( ! res.ok )\n"
"throw \"distinct failed: \" + tojson( res );\n"
"return res.values;\n"
"}\n"
"\n"
+"\n"
"DBCollection.prototype.group = function( params ){\n"
"params.ns = this._shortName;\n"
"return this._db.group( params );\n"
@@ -3365,7 +3751,8 @@ const StringData _jscode_raw_collection =
"*/\n"
"MapReduceResult.prototype.convertToSingleObject = function(){\n"
"var z = {};\n"
-"this._coll.find().forEach( function(a){ z[a._id] = a.value; } );\n"
+"var it = this.results != null ? this.results : this._coll.find();\n"
+"it.forEach( function(a){ z[a._id] = a.value; } );\n"
"return z;\n"
"}\n"
"\n"
@@ -3380,7 +3767,7 @@ const StringData _jscode_raw_collection =
"*/\n"
"DBCollection.prototype.mapReduce = function( map , reduce , optionsOrOutString ){\n"
"var c = { mapreduce : this._shortName , map : map , reduce : reduce };\n"
-"assert( optionsOrOutString , \"need to an optionsOrOutString\" )\n"
+"assert( optionsOrOutString , \"need to supply an optionsOrOutString\" )\n"
"\n"
"if ( typeof( optionsOrOutString ) == \"string\" )\n"
"c[\"out\"] = optionsOrOutString;\n"
diff --git a/shell/msvc/mongo.vcxproj b/shell/msvc/mongo.vcxproj
index af5927c..0718d3a 100644
--- a/shell/msvc/mongo.vcxproj
+++ b/shell/msvc/mongo.vcxproj
@@ -53,7 +53,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>XP_WIN;PCRE_STATIC;HAVE_CONFIG_H;OLDJS;MONGO_EXPOSE_MACROS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>XP_WIN;PCRE_STATIC;HAVE_CONFIG_H;OLDJS;MONGO_EXPOSE_MACROS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>\boost\</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<DisableSpecificWarnings>4355;4800;4267;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
@@ -71,7 +71,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>USE_READLINE;XP_WIN;_WIN32;PCRE_STATIC;HAVE_CONFIG_H;OLDJS;MONGO_EXPOSE_MACROS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>USE_READLINE;XP_WIN;PCRE_STATIC;HAVE_CONFIG_H;OLDJS;MONGO_EXPOSE_MACROS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>\boost\</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -117,15 +117,21 @@
<ClCompile Include="..\..\scripting\engine_spidermonkey.cpp" />
<ClCompile Include="..\..\scripting\utils.cpp" />
<ClCompile Include="..\..\s\shardconnection.cpp" />
+ <ClCompile Include="..\..\third_party\linenoise\linenoise.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="..\..\util\background.cpp" />
+ <ClCompile Include="..\..\util\concurrency\spin_lock.cpp" />
<ClCompile Include="..\..\util\log.cpp" />
<ClCompile Include="..\..\util\mmap.cpp" />
+ <ClCompile Include="..\..\util\net\listen.cpp" />
+ <ClCompile Include="..\..\util\net\message.cpp" />
+ <ClCompile Include="..\..\util\net\message_port.cpp" />
+ <ClCompile Include="..\..\util\net\sock.cpp" />
<ClCompile Include="..\..\util\password.cpp" />
<ClCompile Include="..\..\util\text.cpp" />
<ClCompile Include="..\..\util\mmap_win.cpp" />
<ClCompile Include="..\..\util\processinfo_win32.cpp" />
- <ClCompile Include="..\..\util\sock.cpp" />
- <ClCompile Include="..\..\util\message.cpp" />
<ClCompile Include="..\..\util\assert_util.cpp" />
<ClCompile Include="..\..\util\md5main.cpp" />
<ClCompile Include="..\..\util\md5.c">
diff --git a/shell/msvc/mongo.vcxproj.filters b/shell/msvc/mongo.vcxproj.filters
index 5d0a9a6..57b4ab7 100644
--- a/shell/msvc/mongo.vcxproj.filters
+++ b/shell/msvc/mongo.vcxproj.filters
@@ -39,6 +39,9 @@
<Filter Include="shell\generated_from_js">
<UniqueIdentifier>{96e4c411-7ab4-4bcd-b7c6-a33059f5d492}</UniqueIdentifier>
</Filter>
+ <Filter Include="thirdparty">
+ <UniqueIdentifier>{5eca87ab-5987-4fb0-97be-e80cc721e328}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\dbshell.cpp">
@@ -66,9 +69,6 @@
<ClCompile Include="..\..\db\json.cpp">
<Filter>shared source files</Filter>
</ClCompile>
- <ClCompile Include="..\..\util\sock.cpp">
- <Filter>shell</Filter>
- </ClCompile>
<ClCompile Include="..\..\util\debug_util.cpp">
<Filter>shell</Filter>
</ClCompile>
@@ -162,9 +162,6 @@
<ClCompile Include="..\..\util\md5main.cpp">
<Filter>util</Filter>
</ClCompile>
- <ClCompile Include="..\..\util\message.cpp">
- <Filter>util</Filter>
- </ClCompile>
<ClCompile Include="..\..\util\util.cpp">
<Filter>util</Filter>
</ClCompile>
@@ -225,6 +222,24 @@
<ClCompile Include="..\..\client\dbclient_rs.cpp">
<Filter>client</Filter>
</ClCompile>
+ <ClCompile Include="..\..\third_party\linenoise\linenoise.cpp">
+ <Filter>thirdparty</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\util\concurrency\spin_lock.cpp">
+ <Filter>util\concurrency</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\util\net\listen.cpp">
+ <Filter>util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\util\net\message.cpp">
+ <Filter>util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\util\net\sock.cpp">
+ <Filter>util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\util\net\message_port.cpp">
+ <Filter>util</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\SConstruct" />
diff --git a/shell/query.js b/shell/query.js
index 4044894..78734ca 100644
--- a/shell/query.js
+++ b/shell/query.js
@@ -35,7 +35,6 @@ DBQuery.prototype.help = function () {
print("\t.showDiskLoc() - adds a $diskLoc field to each returned object")
print("\nCursor methods");
print("\t.forEach( func )")
- print("\t.print() - output to console in full pretty format")
print("\t.map( func )")
print("\t.hasNext()")
print("\t.next()")
diff --git a/shell/servers.js b/shell/servers.js
index f713ecc..ad3b5eb 100644..100755
--- a/shell/servers.js
+++ b/shell/servers.js
@@ -1,5 +1,3 @@
-
-
_parsePath = function() {
var dbpath = "";
for( var i = 0; i < arguments.length; ++i )
@@ -23,6 +21,25 @@ _parsePort = function() {
return port;
}
+connectionURLTheSame = function( a , b ){
+ if ( a == b )
+ return true;
+
+ if ( ! a || ! b )
+ return false;
+
+ a = a.split( "/" )[0]
+ b = b.split( "/" )[0]
+
+ return a == b;
+}
+
+assert( connectionURLTheSame( "foo" , "foo" ) )
+assert( ! connectionURLTheSame( "foo" , "bar" ) )
+
+assert( connectionURLTheSame( "foo/a,b" , "foo/b,a" ) )
+assert( ! connectionURLTheSame( "foo/a,b" , "bar/a,b" ) )
+
createMongoArgs = function( binaryName , args ){
var fullArgs = [ binaryName ];
@@ -78,9 +95,12 @@ startMongodTest = function (port, dirname, restart, extraOptions ) {
dbpath: "/data/db/" + dirname,
noprealloc: "",
smallfiles: "",
- oplogSize: "2",
+ oplogSize: "40",
nohttpinterface: ""
};
+
+ if( jsTestOptions().noJournal ) options["nojournal"] = ""
+ if( jsTestOptions().noJournalPrealloc ) options["nopreallocj"] = ""
if ( extraOptions )
Object.extend( options , extraOptions );
@@ -104,7 +124,7 @@ startMongodEmpty = function () {
return startMongoProgram.apply(null, args);
}
startMongod = function () {
- print("WARNING DELETES DATA DIRECTORY THIS IS FOR TESTING RENAME YOUR INVOCATION");
+ print("startMongod WARNING DELETES DATA DIRECTORY THIS IS FOR TESTING ONLY");
return startMongodEmpty.apply(null, arguments);
}
startMongodNoReset = function(){
@@ -135,7 +155,7 @@ startMongoProgram = function(){
} catch( e ) {
}
return false;
- }, "unable to connect to mongo program on port " + port, 300 * 1000 );
+ }, "unable to connect to mongo program on port " + port, 600 * 1000 );
return m;
}
@@ -160,6 +180,19 @@ myPort = function() {
* * useHostname to use the hostname (instead of localhost)
*/
ShardingTest = function( testName , numShards , verboseLevel , numMongos , otherParams ){
+
+ // Check if testName is an object, if so, pull params from there
+ var keyFile = undefined
+ if( testName && ! testName.charAt ){
+ var params = testName
+ testName = params.name || "test"
+ numShards = params.shards || 2
+ verboseLevel = params.verbose || 0
+ numMongos = params.mongos || 1
+ otherParams = params.other || {}
+ keyFile = params.keyFile || otherParams.keyFile
+ }
+
this._testName = testName;
if ( ! otherParams )
@@ -172,16 +205,26 @@ ShardingTest = function( testName , numShards , verboseLevel , numMongos , other
var localhost = otherParams.useHostname ? getHostName() : "localhost";
this._alldbpaths = []
-
-
+
if ( otherParams.rs ){
localhost = getHostName();
// start replica sets
this._rs = []
for ( var i=0; i<numShards; i++){
var setName = testName + "-rs" + i;
- var rs = new ReplSetTest( { name : setName , nodes : 3 , startPort : 31100 + ( i * 100 ) } );
- this._rs[i] = { setName : setName , test : rs , nodes : rs.startSet( { oplogSize:40 } ) , url : rs.getURL() };
+
+ var rsDefaults = { oplogSize : 40, nodes : 3 }
+ var rsParams = otherParams["rs" + i]
+
+ for( var param in rsParams ){
+ rsDefaults[param] = rsParams[param]
+ }
+
+ var numReplicas = rsDefaults.nodes || otherParams.numReplicas || 3
+ delete rsDefaults.nodes
+
+ var rs = new ReplSetTest( { name : setName , nodes : numReplicas , startPort : 31100 + ( i * 100 ), keyFile : keyFile } );
+ this._rs[i] = { setName : setName , test : rs , nodes : rs.startSet( rsDefaults ) , url : rs.getURL() };
rs.initiate();
}
@@ -190,27 +233,35 @@ ShardingTest = function( testName , numShards , verboseLevel , numMongos , other
var rs = this._rs[i].test;
rs.getMaster().getDB( "admin" ).foo.save( { x : 1 } )
rs.awaitReplication();
- this._connections.push( new Mongo( rs.getURL() ) );
+ var xxx = new Mongo( rs.getURL() );
+ xxx.name = rs.getURL();
+ this._connections.push( xxx );
}
this._configServers = []
- for ( var i=0; i<3; i++ ){
- var conn = startMongodTest( 30000 + i , testName + "-config" + i );
+ for ( var i=0; i<3; i++ ){
+ var options = otherParams.extraOptions
+ if( keyFile ) options["keyFile"] = keyFile
+ var conn = startMongodTest( 30000 + i , testName + "-config" + i, false, options );
this._alldbpaths.push( testName + "-config" + i )
this._configServers.push( conn );
}
-
+
this._configDB = localhost + ":30000," + localhost + ":30001," + localhost + ":30002";
this._configConnection = new Mongo( this._configDB );
- this._configConnection.getDB( "config" ).settings.insert( { _id : "chunksize" , value : otherParams.chunksize || 50 } );
+ if (!otherParams.noChunkSize) {
+ this._configConnection.getDB( "config" ).settings.insert( { _id : "chunksize" , value : otherParams.chunksize || 50 } );
+ }
}
else {
for ( var i=0; i<numShards; i++){
- var conn = startMongodTest( 30000 + i , testName + i, 0, {useHostname : otherParams.useHostname} );
+ var options = { useHostname : otherParams.useHostname }
+ if( keyFile ) options["keyFile"] = keyFile
+ var conn = startMongodTest( 30000 + i , testName + i, 0, options );
this._alldbpaths.push( testName +i )
this._connections.push( conn );
}
-
+
if ( otherParams.sync ){
this._configDB = localhost+":30000,"+localhost+":30001,"+localhost+":30002";
this._configConnection = new Mongo( this._configDB );
@@ -226,12 +277,19 @@ ShardingTest = function( testName , numShards , verboseLevel , numMongos , other
var startMongosPort = 31000;
for ( var i=0; i<(numMongos||1); i++ ){
var myPort = startMongosPort - i;
- print("config: "+this._configDB);
- var conn = startMongos( { port : startMongosPort - i , v : verboseLevel || 0 , configdb : this._configDB } );
+ print("ShardingTest config: "+this._configDB);
+ var opts = { port : startMongosPort - i , v : verboseLevel || 0 , configdb : this._configDB };
+ if( keyFile ) opts["keyFile"] = keyFile
+ for (var j in otherParams.extraOptions) {
+ opts[j] = otherParams.extraOptions[j];
+ }
+ var conn = startMongos( opts );
conn.name = localhost + ":" + myPort;
this._mongos.push( conn );
- if ( i == 0 )
+ if ( i == 0 ) {
this.s = conn;
+ }
+ this["s" + i] = conn
}
var admin = this.admin = this.s.getDB( "admin" );
@@ -246,7 +304,7 @@ ShardingTest = function( testName , numShards , verboseLevel , numMongos , other
if ( ! n )
n = z;
}
- print( "going to add shard: " + n )
+ print( "ShardingTest going to add shard: " + n )
x = admin.runCommand( { addshard : n } );
printjson( x )
}
@@ -266,7 +324,7 @@ ShardingTest.prototype.getDB = function( name ){
}
ShardingTest.prototype.getServerName = function( dbname ){
- var x = this.config.databases.findOne( { _id : dbname } );
+ var x = this.config.databases.findOne( { _id : "" + dbname } );
if ( x )
return x.primary;
this.config.databases.find().forEach( printjson );
@@ -300,12 +358,17 @@ ShardingTest.prototype.getServer = function( dbname ){
if ( x )
name = x.host;
+ var rsName = null;
+ if ( name.indexOf( "/" ) > 0 )
+ rsName = name.substring( 0 , name.indexOf( "/" ) );
+
for ( var i=0; i<this._connections.length; i++ ){
var c = this._connections[i];
- if ( name == c.name )
+ if ( connectionURLTheSame( name , c.name ) ||
+ connectionURLTheSame( rsName , c.name ) )
return c;
}
-
+
throw "can't find server for: " + dbname + " name:" + name;
}
@@ -318,15 +381,30 @@ ShardingTest.prototype.normalize = function( x ){
}
ShardingTest.prototype.getOther = function( one ){
- if ( this._connections.length != 2 )
+ if ( this._connections.length < 2 )
throw "getOther only works with 2 servers";
if ( one._mongo )
one = one._mongo
+
+ for( var i = 0; i < this._connections.length; i++ ){
+ if( this._connections[i] != one ) return this._connections[i]
+ }
+
+ return null
+}
- if ( this._connections[0] == one )
- return this._connections[1];
- return this._connections[0];
+ShardingTest.prototype.getAnother = function( one ){
+ if(this._connections.length < 2)
+ throw "getAnother() only works with multiple servers";
+
+ if ( one._mongo )
+ one = one._mongo
+
+ for(var i = 0; i < this._connections.length; i++){
+ if(this._connections[i] == one)
+ return this._connections[(i + 1) % this._connections.length];
+ }
}
ShardingTest.prototype.getFirstOther = function( one ){
@@ -355,7 +433,7 @@ ShardingTest.prototype.stop = function(){
}
}
- print('*** ' + this._testName + " completed successfully ***");
+ print('*** ShardingTest ' + this._testName + " completed successfully ***");
}
ShardingTest.prototype.adminCommand = function(cmd){
@@ -388,7 +466,7 @@ ShardingTest.prototype.printChangeLog = function(){
msg += tojsononeline( z.details );
}
- print( msg )
+ print( "ShardingTest " + msg )
}
);
@@ -410,7 +488,7 @@ ShardingTest.prototype.getChunksString = function( ns ){
}
ShardingTest.prototype.printChunks = function( ns ){
- print( this.getChunksString( ns ) );
+ print( "ShardingTest " + this.getChunksString( ns ) );
}
ShardingTest.prototype.printShardingStatus = function(){
@@ -433,7 +511,7 @@ ShardingTest.prototype.printCollectionInfo = function( ns , msg ){
out += this.getChunksString( ns );
- print( out );
+ print( "ShardingTest " + out );
}
printShardingStatus = function( configDB , verbose ){
@@ -442,7 +520,7 @@ printShardingStatus = function( configDB , verbose ){
var version = configDB.getCollection( "version" ).findOne();
if ( version == null ){
- print( "not a shard db!" );
+ print( "printShardingStatus: not a shard db!" );
return;
}
@@ -454,16 +532,16 @@ printShardingStatus = function( configDB , verbose ){
output( " sharding version: " + tojson( configDB.getCollection( "version" ).findOne() ) );
output( " shards:" );
- configDB.shards.find().forEach(
+ configDB.shards.find().sort( { _id : 1 } ).forEach(
function(z){
- output( " " + tojson(z) );
+ output( "\t" + tojsononeline( z ) );
}
);
output( " databases:" );
configDB.databases.find().sort( { name : 1 } ).forEach(
function(db){
- output( "\t" + tojson(db,"",true) );
+ output( "\t" + tojsononeline(db,"",true) );
if (db.partitioned){
configDB.collections.find( { _id : new RegExp( "^" + db._id + "\." ) } ).sort( { _id : 1 } ).forEach(
@@ -487,7 +565,7 @@ printShardingStatus = function( configDB , verbose ){
);
}
else {
- output( "\t\t\ttoo many chunksn to print, use verbose if you want to force print" );
+ output( "\t\t\ttoo many chunks to print, use verbose if you want to force print" );
}
}
}
@@ -504,7 +582,7 @@ printShardingSizes = function(){
var version = configDB.getCollection( "version" ).findOne();
if ( version == null ){
- print( "not a shard db!" );
+ print( "printShardingSizes : not a shard db!" );
return;
}
@@ -614,23 +692,124 @@ ShardingTest.prototype.chunkDiff = function( collName , dbName ){
if ( c[s] > max )
max = c[s];
}
- print( "input: " + tojson( c ) + " min: " + min + " max: " + max );
+ print( "ShardingTest input: " + tojson( c ) + " min: " + min + " max: " + max );
return max - min;
}
+ShardingTest.prototype.getShard = function( coll, query ){
+ var shards = this.getShards( coll, query )
+ assert.eq( shards.length, 1 )
+ return shards[0]
+}
+
+// Returns the shards on which documents matching a particular query reside
+ShardingTest.prototype.getShards = function( coll, query ){
+ if( ! coll.getDB )
+ coll = this.s.getCollection( coll )
+
+ var explain = coll.find( query ).explain()
+
+ var shards = []
+
+ if( explain.shards ){
+
+ for( var shardName in explain.shards ){
+ for( var i = 0; i < explain.shards[shardName].length; i++ ){
+ if( explain.shards[shardName][i].n && explain.shards[shardName][i].n > 0 )
+ shards.push( shardName )
+ }
+ }
+
+ }
+
+ for( var i = 0; i < shards.length; i++ ){
+ for( var j = 0; j < this._connections.length; j++ ){
+ if ( connectionURLTheSame( this._connections[j].name , shards[i] ) ){
+ shards[i] = this._connections[j]
+ break;
+ }
+ }
+ }
+
+ return shards
+}
+
+ShardingTest.prototype.isSharded = function( collName ){
+
+ var collName = "" + collName
+ var dbName = undefined
+
+ if( typeof collName.getCollectionNames == 'function' ){
+ dbName = "" + collName
+ collName = undefined
+ }
+
+ if( dbName ){
+ var x = this.config.databases.findOne( { _id : dbname } )
+ if( x ) return x.partitioned
+ else return false
+ }
+
+ if( collName ){
+ var x = this.config.collections.findOne( { _id : collName } )
+ if( x ) return true
+ else return false
+ }
+
+}
+
ShardingTest.prototype.shardGo = function( collName , key , split , move , dbName ){
+
split = split || key;
move = move || split;
- dbName = dbName || "test";
+
+ if( collName.getDB )
+ dbName = "" + collName.getDB()
+ else dbName = dbName || "test";
var c = dbName + "." + collName;
+ if( collName.getDB )
+ c = "" + collName
- s.adminCommand( { shardcollection : c , key : key } );
- s.adminCommand( { split : c , middle : split } );
- s.adminCommand( { movechunk : c , find : move , to : this.getOther( s.getServer( dbName ) ).name } );
+ var isEmpty = this.s.getCollection( c ).count() == 0
+
+ if( ! this.isSharded( dbName ) )
+ this.s.adminCommand( { enableSharding : dbName } )
+
+ var result = this.s.adminCommand( { shardcollection : c , key : key } )
+ if( ! result.ok ){
+ printjson( result )
+ assert( false )
+ }
+
+ result = this.s.adminCommand( { split : c , middle : split } );
+ if( ! result.ok ){
+ printjson( result )
+ assert( false )
+ }
+
+ var result = null
+ for( var i = 0; i < 5; i++ ){
+ result = this.s.adminCommand( { movechunk : c , find : move , to : this.getOther( this.getServer( dbName ) ).name } );
+ if( result.ok ) break;
+ sleep( 5 * 1000 );
+ }
+ printjson( result )
+ assert( result.ok )
};
+ShardingTest.prototype.shardColl = ShardingTest.prototype.shardGo
+
+ShardingTest.prototype.setBalancer = function( balancer ){
+ if( balancer || balancer == undefined ){
+ this.config.settings.update( { _id: "balancer" }, { $set : { stopped: false } } , true )
+ }
+ else if( balancer == false ){
+ this.config.settings.update( { _id: "balancer" }, { $set : { stopped: true } } , true )
+ }
+}
+
/**
* Run a mongod process.
*
@@ -666,15 +845,6 @@ MongodRunner.prototype.start = function( reuseData ) {
args.push( this.port_ );
args.push( "--dbpath" );
args.push( this.dbpath_ );
- if ( this.peer_ && this.arbiter_ ) {
- args.push( "--pairwith" );
- args.push( this.peer_ );
- args.push( "--arbiter" );
- args.push( this.arbiter_ );
- args.push( "--oplogSize" );
- // small oplog by default so startup fast
- args.push( "1" );
- }
args.push( "--nohttpinterface" );
args.push( "--noprealloc" );
args.push( "--smallfiles" );
@@ -697,154 +867,6 @@ MongodRunner.prototype.port = function() { return this.port_; }
MongodRunner.prototype.toString = function() { return [ this.port_, this.dbpath_, this.peer_, this.arbiter_ ].toString(); }
-
-ReplPair = function( left, right, arbiter ) {
- this.left_ = left;
- this.leftC_ = null;
- this.right_ = right;
- this.rightC_ = null;
- this.arbiter_ = arbiter;
- this.arbiterC_ = null;
- this.master_ = null;
- this.slave_ = null;
-}
-
-ReplPair.prototype.start = function( reuseData ) {
- if ( this.arbiterC_ == null ) {
- this.arbiterC_ = this.arbiter_.start();
- }
- if ( this.leftC_ == null ) {
- this.leftC_ = this.left_.start( reuseData );
- }
- if ( this.rightC_ == null ) {
- this.rightC_ = this.right_.start( reuseData );
- }
-}
-
-ReplPair.prototype.isMaster = function( mongo, debug ) {
- var im = mongo.getDB( "admin" ).runCommand( { ismaster : 1 } );
- assert( im && im.ok, "command ismaster failed" );
- if ( debug ) {
- printjson( im );
- }
- return im.ismaster;
-}
-
-ReplPair.prototype.isInitialSyncComplete = function( mongo, debug ) {
- var isc = mongo.getDB( "admin" ).runCommand( { isinitialsynccomplete : 1 } );
- assert( isc && isc.ok, "command isinitialsynccomplete failed" );
- if ( debug ) {
- printjson( isc );
- }
- return isc.initialsynccomplete;
-}
-
-ReplPair.prototype.checkSteadyState = function( state, expectedMasterHost, twoMasterOk, leftValues, rightValues, debug ) {
- leftValues = leftValues || {};
- rightValues = rightValues || {};
-
- var lm = null;
- var lisc = null;
- if ( this.leftC_ != null ) {
- lm = this.isMaster( this.leftC_, debug );
- leftValues[ lm ] = true;
- lisc = this.isInitialSyncComplete( this.leftC_, debug );
- }
- var rm = null;
- var risc = null;
- if ( this.rightC_ != null ) {
- rm = this.isMaster( this.rightC_, debug );
- rightValues[ rm ] = true;
- risc = this.isInitialSyncComplete( this.rightC_, debug );
- }
-
- var stateSet = {}
- state.forEach( function( i ) { stateSet[ i ] = true; } );
- if ( !( 1 in stateSet ) || ( ( risc || risc == null ) && ( lisc || lisc == null ) ) ) {
- if ( rm == 1 && lm != 1 ) {
- assert( twoMasterOk || !( 1 in leftValues ) );
- this.master_ = this.rightC_;
- this.slave_ = this.leftC_;
- } else if ( lm == 1 && rm != 1 ) {
- assert( twoMasterOk || !( 1 in rightValues ) );
- this.master_ = this.leftC_;
- this.slave_ = this.rightC_;
- }
- if ( !twoMasterOk ) {
- assert( lm != 1 || rm != 1, "two masters" );
- }
- // check for expected state
- if ( state.sort().toString() == [ lm, rm ].sort().toString() ) {
- if ( expectedMasterHost != null ) {
- if( expectedMasterHost == this.master_.host ) {
- return true;
- }
- } else {
- return true;
- }
- }
- }
-
- this.master_ = null;
- this.slave_ = null;
- return false;
-}
-
-ReplPair.prototype.waitForSteadyState = function( state, expectedMasterHost, twoMasterOk, debug ) {
- state = state || [ 1, 0 ];
- twoMasterOk = twoMasterOk || false;
- var rp = this;
- var leftValues = {};
- var rightValues = {};
- assert.soon( function() { return rp.checkSteadyState( state, expectedMasterHost, twoMasterOk, leftValues, rightValues, debug ); },
- "rp (" + rp + ") failed to reach expected steady state (" + state + ")" , 60000 );
-}
-
-ReplPair.prototype.master = function() { return this.master_; }
-ReplPair.prototype.slave = function() { return this.slave_; }
-ReplPair.prototype.right = function() { return this.rightC_; }
-ReplPair.prototype.left = function() { return this.leftC_; }
-ReplPair.prototype.arbiter = function() { return this.arbiterC_; }
-
-ReplPair.prototype.killNode = function( mongo, signal ) {
- signal = signal || 15;
- if ( this.leftC_ != null && this.leftC_.host == mongo.host ) {
- stopMongod( this.left_.port_ );
- this.leftC_ = null;
- }
- if ( this.rightC_ != null && this.rightC_.host == mongo.host ) {
- stopMongod( this.right_.port_ );
- this.rightC_ = null;
- }
- if ( this.arbiterC_ != null && this.arbiterC_.host == mongo.host ) {
- stopMongod( this.arbiter_.port_ );
- this.arbiterC_ = null;
- }
-}
-
-ReplPair.prototype._annotatedNode = function( mongo ) {
- var ret = "";
- if ( mongo != null ) {
- ret += " (connected)";
- if ( this.master_ != null && mongo.host == this.master_.host ) {
- ret += "(master)";
- }
- if ( this.slave_ != null && mongo.host == this.slave_.host ) {
- ret += "(slave)";
- }
- }
- return ret;
-}
-
-ReplPair.prototype.toString = function() {
- var ret = "";
- ret += "left: " + this.left_;
- ret += " " + this._annotatedNode( this.leftC_ );
- ret += " right: " + this.right_;
- ret += " " + this._annotatedNode( this.rightC_ );
- return ret;
-}
-
ToolTest = function( name ){
this.name = name;
this.port = allocatePorts(1)[0];
@@ -922,7 +944,7 @@ ReplTest.prototype.getOptions = function( master , extra , putBinaryFirst, norep
extra = {};
if ( ! extra.oplogSize )
- extra.oplogSize = "1";
+ extra.oplogSize = "40";
var a = []
if ( putBinaryFirst )
@@ -935,6 +957,8 @@ ReplTest.prototype.getOptions = function( master , extra , putBinaryFirst, norep
a.push( "--dbpath" );
a.push( this.getPath( master ) );
+ if( jsTestOptions().noJournal ) a.push( "--nojournal" )
+ if( jsTestOptions().noJournalPrealloc ) a.push( "--nopreallocj" )
if ( !norepl ) {
if ( master ){
@@ -1050,8 +1074,8 @@ var testingReplication = false;
function skipIfTestingReplication(){
if (testingReplication) {
- print( "skipping" );
- quit(0);
+ print("skipIfTestingReplication skipping");
+ quit(0);
}
}
@@ -1059,10 +1083,11 @@ ReplSetTest = function( opts ){
this.name = opts.name || "testReplSet";
this.host = opts.host || getHostName();
this.numNodes = opts.nodes || 0;
- this.oplogSize = opts.oplogSize || 2;
+ this.oplogSize = opts.oplogSize || 40;
this.useSeedList = opts.useSeedList || false;
this.bridged = opts.bridged || false;
this.ports = [];
+ this.keyFile = opts.keyFile
this.startPort = opts.startPort || 31000;
@@ -1084,6 +1109,10 @@ ReplSetTest = function( opts ){
this.nodes = [];
this.nodeIds = {};
this.initLiveNodes();
+
+ Object.extend( this, ReplSetTest.Health )
+ Object.extend( this, ReplSetTest.State )
+
}
ReplSetTest.prototype.initBridges = function() {
@@ -1109,16 +1138,43 @@ ReplSetTest.prototype.initLiveNodes = function(){
}
ReplSetTest.prototype.getNodeId = function(node) {
- return this.nodeIds[node];
+
+ var result = this.nodeIds[node]
+ if( result ) return result
+
+ if( node.toFixed ) return node
+ return node.nodeId
+
}
ReplSetTest.prototype.getPort = function( n ){
+ if( n.getDB ){
+ // is a connection, look up
+ for( var i = 0; i < this.nodes.length; i++ ){
+ if( this.nodes[i] == n ){
+ n = i
+ break
+ }
+ }
+ }
+
+ if ( typeof(n) == "object" && n.floatApprox )
+ n = n.floatApprox
+
+ // this is a hack for NumberInt
+ if ( n == 0 )
+ n = 0;
+
+ print( "ReplSetTest n: " + n + " ports: " + tojson( this.ports ) + "\t" + this.ports[n] + " " + typeof(n) );
return this.ports[ n ];
}
ReplSetTest.prototype.getPath = function( n ){
- var p = "/data/db/" + this.name + "-";
- p += n.toString();
+
+ if( n.host )
+ n = this.getNodeId( n )
+
+ var p = "/data/db/" + this.name + "-"+n;
if ( ! this._alldbpaths )
this._alldbpaths = [ p ];
else
@@ -1186,17 +1242,22 @@ ReplSetTest.prototype.getOptions = function( n , extra , putBinaryFirst ){
if ( putBinaryFirst )
- a.push( "mongod" )
-
- a.push( "--replSet" );
+ a.push( "mongod" );
- if( this.useSeedList ) {
- a.push( this.getURL() );
+ if ( extra.noReplSet ) {
+ delete extra.noReplSet;
}
else {
- a.push( this.name );
+ a.push( "--replSet" );
+
+ if( this.useSeedList ) {
+ a.push( this.getURL() );
+ }
+ else {
+ a.push( this.name );
+ }
}
-
+
a.push( "--noprealloc", "--smallfiles" );
a.push( "--rest" );
@@ -1205,13 +1266,27 @@ ReplSetTest.prototype.getOptions = function( n , extra , putBinaryFirst ){
a.push( this.getPort( n ) );
a.push( "--dbpath" );
- a.push( this.getPath( n ) );
-
+ a.push( this.getPath( ( n.host ? this.getNodeId( n ) : n ) ) );
+
+ if( this.keyFile ){
+ a.push( "--keyFile" )
+ a.push( keyFile )
+ }
+
+ if( jsTestOptions().noJournal ) a.push( "--nojournal" )
+ if( jsTestOptions().noJournalPrealloc ) a.push( "--nopreallocj" )
+
for ( var k in extra ){
- var v = extra[k];
+ var v = extra[k];
a.push( "--" + k );
- if ( v != null )
+ if ( v != null ){
+ if( v.replace ){
+ v = v.replace(/\$node/g, "" + ( n.host ? this.getNodeId( n ) : n ) )
+ v = v.replace(/\$set/g, this.name )
+ v = v.replace(/\$path/g, this.getPath( n ) )
+ }
a.push( v );
+ }
}
return a;
@@ -1219,7 +1294,7 @@ ReplSetTest.prototype.getOptions = function( n , extra , putBinaryFirst ){
ReplSetTest.prototype.startSet = function(options) {
var nodes = [];
- print( "Starting Set" );
+ print( "ReplSetTest Starting Set" );
for(n=0; n<this.ports.length; n++) {
node = this.start(n, options);
@@ -1231,33 +1306,81 @@ ReplSetTest.prototype.startSet = function(options) {
}
ReplSetTest.prototype.callIsMaster = function() {
+
var master = null;
this.initLiveNodes();
+
for(var i=0; i<this.nodes.length; i++) {
try {
var n = this.nodes[i].getDB('admin').runCommand({ismaster:1});
-
+
if(n['ismaster'] == true) {
master = this.nodes[i];
this.liveNodes.master = master;
this.nodeIds[master] = i;
+ master.nodeId = i
}
else {
this.nodes[i].setSlaveOk();
this.liveNodes.slaves.push(this.nodes[i]);
this.nodeIds[this.nodes[i]] = i;
+ this.nodes[i].nodeId = i
}
}
catch(err) {
- print("Could not call ismaster on node " + i);
+ print("ReplSetTest Could not call ismaster on node " + i);
}
}
return master || false;
}
+ReplSetTest.awaitRSClientHosts = function( conn, host, hostOk, rs ) {
+
+ if( host.length ){
+ for( var i = 0; i < host.length; i++ ) this.awaitOk( conn, host[i] )
+ return
+ }
+
+ if( hostOk == undefined ) hostOk = { ok : true }
+ if( host.host ) host = host.host
+ if( rs && rs.getMaster ) rs = rs.name
+
+ print( "Awaiting " + host + " to be " + tojson( hostOk ) + " for " + conn + " (rs: " + rs + ")" )
+
+ var tests = 0
+ assert.soon( function() {
+ var rsClientHosts = conn.getDB( "admin" ).runCommand( "connPoolStats" )[ "replicaSets" ]
+ if( tests++ % 10 == 0 )
+ printjson( rsClientHosts )
+
+ for ( rsName in rsClientHosts ){
+ if( rs && rs != rsName ) continue
+ for ( var i = 0; i < rsClientHosts[rsName].hosts.length; i++ ){
+ var clientHost = rsClientHosts[rsName].hosts[ i ];
+ if( clientHost.addr != host ) continue
+
+ // Check that *all* host properties are set correctly
+ var propOk = true
+ for( var prop in hostOk ){
+ if( clientHost[prop] != hostOk[prop] ){
+ propOk = false
+ break
+ }
+ }
+
+ if( propOk ) return true;
+
+ }
+ }
+ return false;
+ }, "timed out waiting for replica set client to recognize hosts",
+ 3 * 20 * 1000 /* ReplicaSetMonitorWatcher updates every 20s */ )
+
+}
+
ReplSetTest.prototype.awaitSecondaryNodes = function( timeout ) {
var master = this.getMaster();
var slaves = this.liveNodes.slaves;
@@ -1283,6 +1406,29 @@ ReplSetTest.prototype.getMaster = function( timeout ) {
return master;
}
+ReplSetTest.prototype.getPrimary = ReplSetTest.prototype.getMaster
+
+ReplSetTest.prototype.getSecondaries = function( timeout ){
+ var master = this.getMaster( timeout )
+ var secs = []
+ for( var i = 0; i < this.nodes.length; i++ ){
+ if( this.nodes[i] != master ){
+ secs.push( this.nodes[i] )
+ }
+ }
+ return secs
+}
+
+ReplSetTest.prototype.getSecondary = function( timeout ){
+ return this.getSecondaries( timeout )[0];
+}
+
+ReplSetTest.prototype.status = function( timeout ){
+ var master = this.callIsMaster()
+ if( ! master ) master = this.liveNodes.slaves[0]
+ return master.getDB("admin").runCommand({replSetGetStatus: 1})
+}
+
// Add a node to the test set
ReplSetTest.prototype.add = function( config ) {
if(this.ports.length == 0) {
@@ -1291,13 +1437,13 @@ ReplSetTest.prototype.add = function( config ) {
else {
var nextPort = this.ports[this.ports.length-1] + 1;
}
- print("Next port: " + nextPort);
+ print("ReplSetTest Next port: " + nextPort);
this.ports.push(nextPort);
printjson(this.ports);
var nextId = this.nodes.length;
printjson(this.nodes);
- print(nextId);
+ print("ReplSetTest nextId:" + nextId);
var newNode = this.start(nextId);
this.nodes.push(newNode);
@@ -1323,7 +1469,7 @@ ReplSetTest.prototype.attempt = function( opts, func ) {
tries += 1;
sleep(sleepTime);
if( tries * sleepTime > timeout) {
- throw('[' + opts['desc'] + ']' + " timed out");
+ throw('[' + opts['desc'] + ']' + " timed out after " + timeout + "ms ( " + tries + " tries )");
}
}
@@ -1354,50 +1500,76 @@ ReplSetTest.prototype.reInitiate = function() {
this.initiate( config , 'replSetReconfig' );
}
-ReplSetTest.prototype.awaitReplication = function() {
- this.getMaster();
-
- latest = this.liveNodes.master.getDB("local")['oplog.rs'].find({}).sort({'$natural': -1}).limit(1).next()['ts']
- print(latest);
-
- this.attempt({context: this, timeout: 30000, desc: "awaiting replication"},
- function() {
- var synced = true;
- for(var i=0; i<this.liveNodes.slaves.length; i++) {
- var slave = this.liveNodes.slaves[i];
-
- // Continue if we're connected to an arbiter
- if(res = slave.getDB("admin").runCommand({replSetGetStatus: 1})) {
- if(res.myState == 7) {
- continue;
- }
- }
-
- slave.getDB("admin").getMongo().setSlaveOk();
- var log = slave.getDB("local")['oplog.rs'];
- if(log.find({}).sort({'$natural': -1}).limit(1).hasNext()) {
- var entry = log.find({}).sort({'$natural': -1}).limit(1).next();
- printjson( entry );
- var ts = entry['ts'];
- print("TS for " + slave + " is " + ts.t+":"+ts.i + " and latest is " + latest.t+":"+latest.i);
-
- if (latest.t < ts.t || (latest.t == ts.t && latest.i < ts.i)) {
- latest = this.liveNodes.master.getDB("local")['oplog.rs'].find({}).sort({'$natural': -1}).limit(1).next()['ts'];
- }
-
- print("Oplog size for " + slave + " is " + log.count());
- synced = (synced && friendlyEqual(latest,ts))
- }
- else {
- synced = false;
- }
- }
-
- if(synced) {
- print("Synced = " + synced);
- }
- return synced;
- });
+ReplSetTest.prototype.getLastOpTimeWritten = function() {
+ this.getMaster();
+ this.attempt({context : this, desc : "awaiting oplog query"},
+ function() {
+ try {
+ this.latest = this.liveNodes.master.getDB("local")['oplog.rs'].find({}).sort({'$natural': -1}).limit(1).next()['ts'];
+ }
+ catch(e) {
+ print("ReplSetTest caught exception " + e);
+ return false;
+ }
+ return true;
+ });
+};
+
+ReplSetTest.prototype.awaitReplication = function(timeout) {
+ timeout = timeout || 30000;
+
+ this.getLastOpTimeWritten();
+
+ print("ReplSetTest " + this.latest);
+
+ this.attempt({context: this, timeout: timeout, desc: "awaiting replication"},
+ function() {
+ try {
+ var synced = true;
+ for(var i=0; i<this.liveNodes.slaves.length; i++) {
+ var slave = this.liveNodes.slaves[i];
+
+ // Continue if we're connected to an arbiter
+ if(res = slave.getDB("admin").runCommand({replSetGetStatus: 1})) {
+ if(res.myState == 7) {
+ continue;
+ }
+ }
+
+ slave.getDB("admin").getMongo().setSlaveOk();
+ var log = slave.getDB("local")['oplog.rs'];
+ if(log.find({}).sort({'$natural': -1}).limit(1).hasNext()) {
+ var entry = log.find({}).sort({'$natural': -1}).limit(1).next();
+ printjson( entry );
+ var ts = entry['ts'];
+ print("ReplSetTest await TS for " + slave + " is " + ts.t+":"+ts.i + " and latest is " + this.latest.t+":"+this.latest.i);
+
+ if (this.latest.t < ts.t || (this.latest.t == ts.t && this.latest.i < ts.i)) {
+ this.latest = this.liveNodes.master.getDB("local")['oplog.rs'].find({}).sort({'$natural': -1}).limit(1).next()['ts'];
+ }
+
+ print("ReplSetTest await oplog size for " + slave + " is " + log.count());
+ synced = (synced && friendlyEqual(this.latest,ts))
+ }
+ else {
+ synced = false;
+ }
+ }
+
+ if(synced) {
+ print("ReplSetTest await synced=" + synced);
+ }
+ return synced;
+ }
+ catch (e) {
+ print("ReplSetTest.awaitReplication: caught exception "+e);
+
+ // we might have a new master now
+ this.getLastOpTimeWritten();
+
+ return false;
+ }
+ });
}
ReplSetTest.prototype.getHashes = function( db ){
@@ -1409,55 +1581,462 @@ ReplSetTest.prototype.getHashes = function( db ){
}
/**
- * Starts up a server.
+ * Starts up a server. Options are saved by default for subsequent starts.
+ *
+ *
+ * Options { remember : true } re-applies the saved options from a prior start.
+ * Options { noRemember : true } ignores the current properties.
+ * Options { appendOptions : true } appends the current options to those remembered.
+ * Options { startClean : true } clears the data directory before starting.
*
- * @param {int} n server number (0, 1, 2, ...)
+ * @param @param {int|conn|[int|conn]} n array or single server number (0, 1, 2, ...) or conn
* @param {object} [options]
* @param {boolean} [restart] If false, the data directory will be cleared
* before the server starts. Defaults to false.
+ *
*/
-ReplSetTest.prototype.start = function( n , options , restart ){
+ReplSetTest.prototype.start = function( n , options , restart , wait ){
+
+ if( n.length ){
+
+ var nodes = n
+ var started = []
+
+ for( var i = 0; i < nodes.length; i++ ){
+ if( this.start( nodes[i], Object.extend({}, options), restart, wait ) ){
+ started.push( nodes[i] )
+ }
+ }
+
+ return started
+
+ }
+
+ print( "ReplSetTest n is : " + n )
+
var lockFile = this.getPath( n ) + "/mongod.lock";
removeFile( lockFile );
- var o = this.getOptions( n , options , restart );
+
+ options = options || {}
+ var noRemember = options.noRemember
+ delete options.noRemember
+ var appendOptions = options.appendOptions
+ delete options.appendOptions
+ var startClean = options.startClean
+ delete options.startClean
+
+ if( restart && options.remember ){
+ delete options.remember
+
+ var oldOptions = {}
+ if( this.savedStartOptions && this.savedStartOptions[n] ){
+ oldOptions = this.savedStartOptions[n]
+ }
+
+ var newOptions = options
+ var options = {}
+ Object.extend( options, oldOptions )
+ Object.extend( options, newOptions )
+
+ }
+
+ var shouldRemember = ( ! restart && ! noRemember ) || ( restart && appendOptions )
+
+ if ( shouldRemember ){
+ this.savedStartOptions = this.savedStartOptions || {}
+ this.savedStartOptions[n] = options
+ }
+
+ if( tojson(options) != tojson({}) )
+ printjson(options)
+
+ var o = this.getOptions( n , options , restart && ! startClean );
- print("Starting....");
- print( o );
+ print("ReplSetTest " + (restart ? "(Re)" : "") + "Starting....");
+ print("ReplSetTest " + o );
+
+ var rval = null
if ( restart ) {
- this.nodes[n] = startMongoProgram.apply( null , o );
- printjson(this.nodes);
- return this.nodes[n];
+ n = this.getNodeId( n )
+ this.nodes[n] = ( startClean ? startMongod.apply( null , o ) : startMongoProgram.apply( null , o ) );
+ this.nodes[n].host = this.nodes[n].host.replace( "127.0.0.1", this.host )
+ if( shouldRemember ) this.savedStartOptions[this.nodes[n]] = options
+ printjson( this.nodes )
+ rval = this.nodes[n];
}
else {
- return startMongod.apply( null , o );
+ var conn = startMongod.apply( null , o );
+ if( shouldRemember ) this.savedStartOptions[conn] = options
+ conn.host = conn.host.replace( "127.0.0.1", this.host )
+ rval = conn;
+ }
+
+ wait = wait || false
+ if( ! wait.toFixed ){
+ if( wait ) wait = 0
+ else wait = -1
}
+
+ if( rval == null || wait < 0 ) return rval
+
+ // Wait for startup
+ this.waitForHealth( rval, this.UP, wait )
+
+ return rval
+
}
+
/**
- * Restarts a db without clearing the data directory. If the server is not
- * stopped first, this function will not work.
+ * Restarts a db without clearing the data directory by default. If the server is not
+ * stopped first, this function will not work.
*
- * @param {int} n server number (0, 1, 2, ...)
+ * Option { startClean : true } forces clearing the data directory.
+ *
+ * @param {int|conn|[int|conn]} n array or single server number (0, 1, 2, ...) or conn
*/
-ReplSetTest.prototype.restart = function( n , options ){
- return this.start( n , options , true );
+ReplSetTest.prototype.restart = function( n , options, signal, wait ){
+ // Can specify wait as third parameter, if using default signal
+ if( signal == true || signal == false ){
+ wait = signal
+ signal = undefined
+ }
+
+ this.stop( n, signal, wait && wait.toFixed ? wait : true )
+ return this.start( n , options , true, wait );
}
-ReplSetTest.prototype.stop = function( n , signal ){
+ReplSetTest.prototype.stopMaster = function( signal , wait ) {
+ var master = this.getMaster();
+ var master_id = this.getNodeId( master );
+ return this.stop( master_id , signal , wait );
+}
+
+// Stops a particular node or nodes, specified by conn or id
+ReplSetTest.prototype.stop = function( n , signal, wait /* wait for stop */ ){
+
+ // Flatten array of nodes to stop
+ if( n.length ){
+ nodes = n
+
+ var stopped = []
+ for( var i = 0; i < nodes.length; i++ ){
+ if( this.stop( nodes[i], signal, wait ) )
+ stopped.push( nodes[i] )
+ }
+
+ return stopped
+ }
+
+
+ // Can specify wait as second parameter, if using default signal
+ if( signal == true || signal == false ){
+ wait = signal
+ signal = undefined
+ }
+
+ wait = wait || false
+ if( ! wait.toFixed ){
+ if( wait ) wait = 0
+ else wait = -1
+ }
+
var port = this.getPort( n );
- print('*** Shutting down mongod in port ' + port + ' ***');
- return stopMongod( port , signal || 15 );
+ print('ReplSetTest stop *** Shutting down mongod in port ' + port + ' ***');
+ var ret = stopMongod( port , signal || 15 );
+
+ if( ! ret || wait < 0 ) return ret
+
+ // Wait for shutdown
+ this.waitForHealth( n, this.DOWN, wait )
+
+ return true
}
+
ReplSetTest.prototype.stopSet = function( signal , forRestart ) {
for(i=0; i < this.ports.length; i++) {
this.stop( i, signal );
}
if ( ! forRestart && this._alldbpaths ){
+ print("ReplSetTest stopSet deleting all dbpaths");
for( i=0; i<this._alldbpaths.length; i++ ){
resetDbpath( this._alldbpaths[i] );
}
}
- print('*** Shut down repl set - test worked ****' )
+ print('ReplSetTest stopSet *** Shut down repl set - test worked ****' )
+};
+
+
+/**
+ * Waits until there is a master node
+ */
+ReplSetTest.prototype.waitForMaster = function( timeout ){
+
+ var master = undefined
+
+ this.attempt({context: this, timeout: timeout, desc: "waiting for master"}, function() {
+ return ( master = this.getMaster() )
+ });
+
+ return master
+}
+
+
+/**
+ * Wait for a health indicator to go to a particular state or states.
+ *
+ * @param node is a single node or list of nodes, by id or conn
+ * @param state is a single state or list of states
+ *
+ */
+ReplSetTest.prototype.waitForHealth = function( node, state, timeout ){
+ this.waitForIndicator( node, state, "health", timeout )
+}
+
+/**
+ * Wait for a state indicator to go to a particular state or states.
+ *
+ * @param node is a single node or list of nodes, by id or conn
+ * @param state is a single state or list of states
+ *
+ */
+ReplSetTest.prototype.waitForState = function( node, state, timeout ){
+ this.waitForIndicator( node, state, "state", timeout )
+}
+
+/**
+ * Wait for a rs indicator to go to a particular state or states.
+ *
+ * @param node is a single node or list of nodes, by id or conn
+ * @param states is a single state or list of states
+ * @param ind is the indicator specified
+ *
+ */
+ReplSetTest.prototype.waitForIndicator = function( node, states, ind, timeout ){
+
+ if( node.length ){
+
+ var nodes = node
+ for( var i = 0; i < nodes.length; i++ ){
+ if( states.length )
+ this.waitForIndicator( nodes[i], states[i], ind, timeout )
+ else
+ this.waitForIndicator( nodes[i], states, ind, timeout )
+ }
+
+ return;
+ }
+
+ timeout = timeout || 30000;
+
+ if( ! node.getDB ){
+ node = this.nodes[node]
+ }
+
+ if( ! states.length ) states = [ states ]
+
+ print( "ReplSetTest waitForIndicator " + ind )
+ printjson( states )
+ print( "ReplSetTest waitForIndicator from node " + node )
+
+ var lastTime = null
+ var currTime = new Date().getTime()
+ var status = undefined
+
+ this.attempt({context: this, timeout: timeout, desc: "waiting for state indicator " + ind + " for " + timeout + "ms" }, function() {
+
+ status = this.status()
+
+ if( lastTime == null || ( currTime = new Date().getTime() ) - (1000 * 5) > lastTime ){
+ if( lastTime == null ) print( "ReplSetTest waitForIndicator Initial status ( timeout : " + timeout + " ) :" )
+ printjson( status )
+ lastTime = new Date().getTime()
+ }
+
+ for( var i = 0; i < status.members.length; i++ ){
+ if( status.members[i].name == node.host ){
+ for( var j = 0; j < states.length; j++ ){
+ if( status.members[i][ind] == states[j] ) return true;
+ }
+ }
+ }
+
+ return false
+
+ });
+
+ print( "ReplSetTest waitForIndicator final status:" )
+ printjson( status )
+
+}
+
+ReplSetTest.Health = {}
+ReplSetTest.Health.UP = 1
+ReplSetTest.Health.DOWN = 0
+
+ReplSetTest.State = {}
+ReplSetTest.State.PRIMARY = 1
+ReplSetTest.State.SECONDARY = 2
+ReplSetTest.State.RECOVERING = 3
+
+/**
+ * Overflows a replica set secondary or secondaries, specified by id or conn.
+ */
+ReplSetTest.prototype.overflow = function( secondaries ){
+
+ // Create a new collection to overflow, allow secondaries to replicate
+ var master = this.getMaster()
+ var overflowColl = master.getCollection( "_overflow.coll" )
+ overflowColl.insert({ replicated : "value" })
+ this.awaitReplication()
+
+ this.stop( secondaries, undefined, 5 * 60 * 1000 )
+
+ var count = master.getDB("local").oplog.rs.count();
+ var prevCount = -1;
+
+ // Keep inserting till we hit our capped coll limits
+ while (count != prevCount) {
+
+ print("ReplSetTest overflow inserting 10000");
+
+ for (var i = 0; i < 10000; i++) {
+ overflowColl.insert({ overflow : "value" });
+ }
+ prevCount = count;
+ this.awaitReplication();
+
+ count = master.getDB("local").oplog.rs.count();
+
+ print( "ReplSetTest overflow count : " + count + " prev : " + prevCount );
+
+ }
+
+ // Restart all our secondaries and wait for recovery state
+ this.start( secondaries, { remember : true }, true, true )
+ this.waitForState( secondaries, this.RECOVERING, 5 * 60 * 1000 )
+
}
+
+
+
+
+/**
+ * Bridging allows you to test network partitioning. For example, you can set
+ * up a replica set, run bridge(), then kill the connection between any two
+ * nodes x and y with partition(x, y).
+ *
+ * Once you have called bridging, you cannot reconfigure the replica set.
+ */
+ReplSetTest.prototype.bridge = function() {
+ if (this.bridges) {
+ print("ReplSetTest bridge bridges have already been created!");
+ return;
+ }
+
+ var n = this.nodes.length;
+
+ // create bridges
+ this.bridges = [];
+ for (var i=0; i<n; i++) {
+ var nodeBridges = [];
+ for (var j=0; j<n; j++) {
+ if (i == j) {
+ continue;
+ }
+ nodeBridges[j] = new ReplSetBridge(this, i, j);
+ }
+ this.bridges.push(nodeBridges);
+ }
+ print("ReplSetTest bridge bridges: " + this.bridges);
+
+ // restart everyone independently
+ this.stopSet(null, true);
+ for (var i=0; i<n; i++) {
+ this.restart(i, {noReplSet : true});
+ }
+
+ // create new configs
+ for (var i=0; i<n; i++) {
+ config = this.nodes[i].getDB("local").system.replset.findOne();
+
+ if (!config) {
+ print("ReplSetTest bridge couldn't find config for "+this.nodes[i]);
+ printjson(this.nodes[i].getDB("local").system.namespaces.find().toArray());
+ assert(false);
+ }
+
+ var updateMod = {"$set" : {}};
+ for (var j = 0; j<config.members.length; j++) {
+ if (config.members[j].host == this.host+":"+this.ports[i]) {
+ continue;
+ }
+
+ updateMod['$set']["members."+j+".host"] = this.bridges[i][j].host;
+ }
+ print("ReplSetTest bridge for node " + i + ":");
+ printjson(updateMod);
+ this.nodes[i].getDB("local").system.replset.update({},updateMod);
+ }
+
+ this.stopSet(null, true);
+
+ // start set
+ for (var i=0; i<n; i++) {
+ this.restart(i);
+ }
+
+ return this.getMaster();
+};
+
+/**
+ * This kills the bridge between two nodes. As parameters, specify the from and
+ * to node numbers.
+ *
+ * For example, with a three-member replica set, we'd have nodes 0, 1, and 2,
+ * with the following bridges: 0->1, 0->2, 1->0, 1->2, 2->0, 2->1. We can kill
+ * the connection between nodes 0 and 2 by calling replTest.partition(0,2) or
+ * replTest.partition(2,0) (either way is identical). Then the replica set would
+ * have the following bridges: 0->1, 1->0, 1->2, 2->1.
+ */
+ReplSetTest.prototype.partition = function(from, to) {
+ this.bridges[from][to].stop();
+ this.bridges[to][from].stop();
+};
+
+/**
+ * This reverses a partition created by partition() above.
+ */
+ReplSetTest.prototype.unPartition = function(from, to) {
+ this.bridges[from][to].start();
+ this.bridges[to][from].start();
+};
+
+ReplSetBridge = function(rst, from, to) {
+ var n = rst.nodes.length;
+
+ var startPort = rst.startPort+n;
+ this.port = (startPort+(from*n+to));
+ this.host = rst.host+":"+this.port;
+
+ this.dest = rst.host+":"+rst.ports[to];
+ this.start();
+};
+
+ReplSetBridge.prototype.start = function() {
+ var args = ["mongobridge", "--port", this.port, "--dest", this.dest];
+ print("ReplSetBridge starting: "+tojson(args));
+ this.bridge = startMongoProgram.apply( null , args );
+ print("ReplSetBridge started " + this.bridge);
+};
+
+ReplSetBridge.prototype.stop = function() {
+ print("ReplSetBridge stopping: " + this.port);
+ stopMongod(this.port);
+};
+
+ReplSetBridge.prototype.toString = function() {
+ return this.host+" -> "+this.dest;
+};
diff --git a/shell/shell_utils.cpp b/shell/shell_utils.cpp
index 09a3e46..e09309c 100644
--- a/shell/shell_utils.cpp
+++ b/shell/shell_utils.cpp
@@ -60,6 +60,10 @@ namespace mongo {
inline int pipe(int fds[2]) { return _pipe(fds, 4096, _O_TEXT | _O_NOINHERIT); }
#endif
+ namespace JSFiles {
+ extern const JSFile servers;
+ }
+
// these functions have not been audited for thread safety - currently they are called with an exclusive js mutex
namespace shellUtils {
@@ -86,19 +90,8 @@ namespace mongo {
// real methods
- mongo::BSONObj JSSleep(const mongo::BSONObj &args) {
- assert( args.nFields() == 1 );
- assert( args.firstElement().isNumber() );
- int ms = int( args.firstElement().number() );
- {
- auto_ptr< ScriptEngine::Unlocker > u = globalScriptEngine->newThreadUnlocker();
- sleepmillis( ms );
- }
- return undefined_;
- }
-
void goingAwaySoon();
- BSONObj Quit(const BSONObj& args) {
+ BSONObj Quit(const BSONObj& args, void* data) {
// If not arguments are given first element will be EOO, which
// converts to the integer value 0.
goingAwaySoon();
@@ -107,7 +100,7 @@ namespace mongo {
return undefined_;
}
- BSONObj JSGetMemInfo( const BSONObj& args ) {
+ BSONObj JSGetMemInfo( const BSONObj& args, void* data ) {
ProcessInfo pi;
uassert( 10258 , "processinfo not supported" , pi.supported() );
@@ -124,13 +117,13 @@ namespace mongo {
#ifndef MONGO_SAFE_SHELL
- BSONObj listFiles(const BSONObj& _args) {
+ BSONObj listFiles(const BSONObj& _args, void* data) {
static BSONObj cd = BSON( "0" << "." );
BSONObj args = _args.isEmpty() ? cd : _args;
uassert( 10257 , "need to specify 1 argument to listFiles" , args.nFields() == 1 );
- BSONObjBuilder lst;
+ BSONArrayBuilder lst;
string rootname = args.firstElement().valuestrsafe();
path root( rootname );
@@ -142,7 +135,6 @@ namespace mongo {
directory_iterator end;
directory_iterator i( root);
- int num =0;
while ( i != end ) {
path p = *i;
BSONObjBuilder b;
@@ -158,21 +150,17 @@ namespace mongo {
}
}
- stringstream ss;
- ss << num;
- string name = ss.str();
- lst.append( name, b.done() );
- num++;
+ lst.append( b.obj() );
i++;
}
-
+
BSONObjBuilder ret;
ret.appendArray( "", lst.done() );
return ret.obj();
}
- BSONObj ls(const BSONObj& args) {
- BSONObj o = listFiles(args);
+ BSONObj ls(const BSONObj& args, void* data) {
+ BSONObj o = listFiles(args, data);
if( !o.isEmpty() ) {
for( BSONObj::iterator i = o.firstElement().Obj().begin(); i.more(); ) {
BSONObj f = i.next().Obj();
@@ -185,7 +173,7 @@ namespace mongo {
return BSONObj();
}
- BSONObj cd(const BSONObj& args) {
+ BSONObj cd(const BSONObj& args, void* data) {
#if defined(_WIN32)
std::wstring dir = toWideString( args.firstElement().String().c_str() );
if( SetCurrentDirectory(dir.c_str()) )
@@ -200,12 +188,12 @@ namespace mongo {
return BSON( "" << "change directory failed" );
}
- BSONObj pwd(const BSONObj&) {
+ BSONObj pwd(const BSONObj&, void* data) {
boost::filesystem::path p = boost::filesystem::current_path();
return BSON( "" << p.string() );
}
- BSONObj hostname(const BSONObj&) {
+ BSONObj hostname(const BSONObj&, void* data) {
return BSON( "" << getHostName() );
}
@@ -216,7 +204,7 @@ namespace mongo {
const int CANT_OPEN_FILE = 13300;
- BSONObj cat(const BSONObj& args) {
+ BSONObj cat(const BSONObj& args, void* data) {
BSONElement e = oneArg(args);
stringstream ss;
ifstream f(e.valuestrsafe());
@@ -235,7 +223,7 @@ namespace mongo {
return BSON( "" << ss.str() );
}
- BSONObj md5sumFile(const BSONObj& args) {
+ BSONObj md5sumFile(const BSONObj& args, void* data) {
BSONElement e = oneArg(args);
stringstream ss;
FILE* f = fopen(e.valuestrsafe(), "rb");
@@ -256,12 +244,12 @@ namespace mongo {
return BSON( "" << digestToString( d ) );
}
- BSONObj mkdir(const BSONObj& args) {
+ BSONObj mkdir(const BSONObj& args, void* data) {
boost::filesystem::create_directories(args.firstElement().String());
return BSON( "" << true );
}
- BSONObj removeFile(const BSONObj& args) {
+ BSONObj removeFile(const BSONObj& args, void* data) {
BSONElement e = oneArg(args);
bool found = false;
@@ -280,7 +268,7 @@ namespace mongo {
* @param args - [ name, byte index ]
* In this initial implementation, all bits in the specified byte are flipped.
*/
- BSONObj fuzzFile(const BSONObj& args) {
+ BSONObj fuzzFile(const BSONObj& args, void* data) {
uassert( 13619, "fuzzFile takes 2 arguments", args.nFields() == 2 );
shared_ptr< File > f( new File() );
f->open( args.getStringField( "0" ) );
@@ -314,7 +302,7 @@ namespace mongo {
if( mongo::dbexitCalled ) throw "program is terminating";
stringstream buf;
if ( port > 0 )
- buf << "m" << port << "| " << line;
+ buf << " m" << port << "| " << line;
else
buf << "sh" << pid << "| " << line;
cout << buf.str() << endl;
@@ -322,7 +310,7 @@ namespace mongo {
}
// only returns last 100000 characters
- BSONObj RawMongoProgramOutput( const BSONObj &args ) {
+ BSONObj RawMongoProgramOutput( const BSONObj &args, void* data ) {
mongo::mutex::scoped_lock lk( mongoProgramOutputMutex );
string out = mongoProgramOutput_.str();
size_t len = out.length();
@@ -331,7 +319,7 @@ namespace mongo {
return BSON( "" << out );
}
- BSONObj ClearRawMongoProgramOutput( const BSONObj &args ) {
+ BSONObj ClearRawMongoProgramOutput( const BSONObj &args, void* data ) {
mongo::mutex::scoped_lock lk( mongoProgramOutputMutex );
mongoProgramOutput_.str( "" );
return undefined_;
@@ -466,12 +454,16 @@ namespace mongo {
try {
// This assumes there aren't any 0's in the mongo program output.
// Hope that's ok.
- const unsigned bufSize = 64000;
+ const unsigned bufSize = 128 * 1024;
char buf[ bufSize ];
char temp[ bufSize ];
char *start = buf;
while( 1 ) {
int lenToRead = ( bufSize - 1 ) - ( start - buf );
+ if ( lenToRead <= 0 ) {
+ cout << "error: lenToRead: " << lenToRead << endl;
+ cout << "first 300: " << string(buf,0,300) << endl;
+ }
assert( lenToRead > 0 );
int ret = read( pipe_, (void *)start, lenToRead );
if( mongo::dbexitCalled )
@@ -541,7 +533,7 @@ namespace mongo {
{
stringstream ss;
ss << "couldn't start process " << argv_[0];
- uassert(13294, ss.str(), success);
+ uassert(14042, ss.str(), success);
}
CloseHandle(pi.hThread);
@@ -624,14 +616,14 @@ namespace mongo {
#endif
}
- BSONObj WaitProgram( const BSONObj& a ) {
+ BSONObj WaitProgram( const BSONObj& a, void* data ) {
int pid = oneArg( a ).numberInt();
BSONObj x = BSON( "" << wait_for_pid( pid ) );
shells.erase( pid );
return x;
}
- BSONObj WaitMongoProgramOnPort( const BSONObj &a ) {
+ BSONObj WaitMongoProgramOnPort( const BSONObj &a, void* data ) {
int port = oneArg( a ).numberInt();
uassert( 13621, "no known mongo program on port", dbs.count( port ) != 0 );
log() << "waiting port: " << port << ", pid: " << dbs[ port ].first << endl;
@@ -642,7 +634,7 @@ namespace mongo {
return BSON( "" << ret );
}
- BSONObj StartMongoProgram( const BSONObj &a ) {
+ BSONObj StartMongoProgram( const BSONObj &a, void* data ) {
_nokillop = true;
ProgramRunner r( a );
r.start();
@@ -650,7 +642,7 @@ namespace mongo {
return BSON( string( "" ) << int( r.pid() ) );
}
- BSONObj RunMongoProgram( const BSONObj &a ) {
+ BSONObj RunMongoProgram( const BSONObj &a, void* data ) {
ProgramRunner r( a );
r.start();
boost::thread t( r );
@@ -665,7 +657,7 @@ namespace mongo {
return BSON( string( "" ) << exit_code );
}
- BSONObj RunProgram(const BSONObj &a) {
+ BSONObj RunProgram(const BSONObj &a, void* data) {
ProgramRunner r( a, false );
r.start();
boost::thread t( r );
@@ -675,7 +667,7 @@ namespace mongo {
return BSON( string( "" ) << exit_code );
}
- BSONObj ResetDbpath( const BSONObj &a ) {
+ BSONObj ResetDbpath( const BSONObj &a, void* data ) {
assert( a.nFields() == 1 );
string path = a.firstElement().valuestrsafe();
assert( !path.empty() );
@@ -705,7 +697,7 @@ namespace mongo {
}
// NOTE target dbpath will be cleared first
- BSONObj CopyDbpath( const BSONObj &a ) {
+ BSONObj CopyDbpath( const BSONObj &a, void* data ) {
assert( a.nFields() == 2 );
BSONObjIterator i( a );
string from = i.next().str();
@@ -816,22 +808,22 @@ namespace mongo {
}
/** stopMongoProgram(port[, signal]) */
- BSONObj StopMongoProgram( const BSONObj &a ) {
+ BSONObj StopMongoProgram( const BSONObj &a, void* data ) {
assert( a.nFields() == 1 || a.nFields() == 2 );
- assert( a.firstElement().isNumber() );
+ uassert( 15853 , "stopMongo needs a number" , a.firstElement().isNumber() );
int port = int( a.firstElement().number() );
int code = killDb( port, 0, getSignal( a ) );
cout << "shell: stopped mongo program on port " << port << endl;
- return BSON( "" << code );
+ return BSON( "" << (double)code );
}
- BSONObj StopMongoProgramByPid( const BSONObj &a ) {
+ BSONObj StopMongoProgramByPid( const BSONObj &a, void* data ) {
assert( a.nFields() == 1 || a.nFields() == 2 );
- assert( a.firstElement().isNumber() );
+ uassert( 15852 , "stopMongoByPid needs a number" , a.firstElement().isNumber() );
int pid = int( a.firstElement().number() );
int code = killDb( 0, pid, getSignal( a ) );
cout << "shell: stopped mongo program on pid " << pid << endl;
- return BSON( "" << code );
+ return BSON( "" << (double)code );
}
void KillMongoProgramInstances() {
@@ -853,20 +845,20 @@ namespace mongo {
MongoProgramScope::~MongoProgramScope() {
DESTRUCTOR_GUARD(
KillMongoProgramInstances();
- ClearRawMongoProgramOutput( BSONObj() );
+ ClearRawMongoProgramOutput( BSONObj(), 0 );
)
}
unsigned _randomSeed;
- BSONObj JSSrand( const BSONObj &a ) {
+ BSONObj JSSrand( const BSONObj &a, void* data ) {
uassert( 12518, "srand requires a single numeric argument",
a.nFields() == 1 && a.firstElement().isNumber() );
_randomSeed = (unsigned)a.firstElement().numberLong(); // grab least significant digits
return undefined_;
}
- BSONObj JSRand( const BSONObj &a ) {
+ BSONObj JSRand( const BSONObj &a, void* data ) {
uassert( 12519, "rand accepts no arguments", a.nFields() == 0 );
unsigned r;
#if !defined(_WIN32)
@@ -877,7 +869,7 @@ namespace mongo {
return BSON( "" << double( r ) / ( double( RAND_MAX ) + 1 ) );
}
- BSONObj isWindows(const BSONObj& a) {
+ BSONObj isWindows(const BSONObj& a, void* data) {
uassert( 13006, "isWindows accepts no arguments", a.nFields() == 0 );
#ifdef _WIN32
return BSON( "" << true );
@@ -886,7 +878,7 @@ namespace mongo {
#endif
}
- BSONObj getHostName(const BSONObj& a) {
+ BSONObj getHostName(const BSONObj& a, void* data) {
uassert( 13411, "getHostName accepts no arguments", a.nFields() == 0 );
char buf[260]; // HOST_NAME_MAX is usually 255
assert(gethostname(buf, 260) == 0);
@@ -897,7 +889,6 @@ namespace mongo {
void installShellUtils( Scope& scope ) {
theScope = &scope;
- scope.injectNative( "sleep" , JSSleep );
scope.injectNative( "quit", Quit );
scope.injectNative( "getMemInfo" , JSGetMemInfo );
scope.injectNative( "_srand" , JSSrand );
diff --git a/shell/utils.js b/shell/utils.js
index 6b52ab9..8380607 100644
--- a/shell/utils.js
+++ b/shell/utils.js
@@ -9,10 +9,24 @@ chatty = function(s){
friendlyEqual = function( a , b ){
if ( a == b )
return true;
+
+ a = tojson(a,false,true);
+ b = tojson(b,false,true);
- if ( tojson( a ) == tojson( b ) )
+ if ( a == b )
return true;
+ var clean = function( s ){
+ s = s.replace( /NumberInt\((\-?\d+)\)/g , "$1" );
+ return s;
+ }
+
+ a = clean(a);
+ b = clean(b);
+
+ if ( a == b )
+ return true;
+
return false;
}
@@ -35,10 +49,8 @@ doassert = function (msg) {
assert = function( b , msg ){
if ( assert._debug && msg ) print( "in assert for: " + msg );
-
if ( b )
- return;
-
+ return;
doassert( msg == undefined ? "assert failed" : "assert failed : " + msg );
}
@@ -72,6 +84,26 @@ assert.neq = function( a , b , msg ){
doassert( "[" + a + "] != [" + b + "] are equal : " + msg );
}
+assert.contains = function( o, arr, msg ){
+ var wasIn = false
+
+ if( ! arr.length ){
+ for( i in arr ){
+ wasIn = arr[i] == o || ( ( arr[i] != null && o != null ) && friendlyEqual( arr[i] , o ) )
+ return;
+ if( wasIn ) break
+ }
+ }
+ else {
+ for( var i = 0; i < arr.length; i++ ){
+ wasIn = arr[i] == o || ( ( arr[i] != null && o != null ) && friendlyEqual( arr[i] , o ) )
+ if( wasIn ) break
+ }
+ }
+
+ if( ! wasIn ) doassert( tojson( o ) + " was not in " + tojson( arr ) + " : " + msg )
+}
+
assert.repeat = function( f, msg, timeout, interval ) {
if ( assert._debug && msg ) print( "in assert for: " + msg );
@@ -199,6 +231,18 @@ assert.gte = function( a , b , msg ){
doassert( a + " is not greater than or eq " + b + " : " + msg );
}
+assert.between = function( a, b, c, msg, inclusive ){
+ if ( assert._debug && msg ) print( "in assert for: " + msg );
+
+ if( ( inclusive == undefined || inclusive == true ) &&
+ a <= b && b <= c ) return;
+ else if( a < b && b < c ) return;
+
+ doassert( b + " is not between " + a + " and " + c + " : " + msg );
+}
+
+assert.betweenIn = function( a, b, c, msg ){ assert.between( a, b, c, msg, true ) }
+assert.betweenEx = function( a, b, c, msg ){ assert.between( a, b, c, msg, false ) }
assert.close = function( a , b , msg , places ){
if (places === undefined) {
@@ -226,6 +270,11 @@ Object.extend = function( dst , src , deep ){
return dst;
}
+Object.merge = function( dst, src, deep ){
+ var clone = Object.extend( {}, dst, deep )
+ return Object.extend( clone, src, deep )
+}
+
argumentsToArray = function( a ){
var arr = [];
for ( var i=0; i<a.length; i++ )
@@ -370,20 +419,25 @@ Array.shuffle = function( arr ){
}
-Array.tojson = function( a , indent ){
+Array.tojson = function( a , indent , nolint ){
+ var lineEnding = nolint ? " " : "\n";
+
if (!indent)
indent = "";
+
+ if ( nolint )
+ indent = "";
if (a.length == 0) {
return "[ ]";
}
- var s = "[\n";
+ var s = "[" + lineEnding;
indent += "\t";
for ( var i=0; i<a.length; i++){
- s += indent + tojson( a[i], indent );
+ s += indent + tojson( a[i], indent , nolint );
if ( i < a.length - 1 ){
- s += ",\n";
+ s += "," + lineEnding;
}
}
if ( a.length == 0 ) {
@@ -391,7 +445,7 @@ Array.tojson = function( a , indent ){
}
indent = indent.substring(1);
- s += "\n"+indent+"]";
+ s += lineEnding+indent+"]";
return s;
}
@@ -459,6 +513,14 @@ NumberLong.prototype.tojson = function() {
return this.toString();
}
+if ( ! NumberInt.prototype ) {
+ NumberInt.prototype = {}
+}
+
+NumberInt.prototype.tojson = function() {
+ return this.toString();
+}
+
if ( ! ObjectId.prototype )
ObjectId.prototype = {}
@@ -533,16 +595,24 @@ if ( typeof( BinData ) != "undefined" ){
//return "BinData type: " + this.type + " len: " + this.len;
return this.toString();
}
+
+ BinData.prototype.subtype = function () {
+ return this.type;
+ }
+
+ BinData.prototype.length = function () {
+ return this.len;
+ }
}
else {
print( "warning: no BinData class" );
}
-if ( typeof( UUID ) != "undefined" ){
+/*if ( typeof( UUID ) != "undefined" ){
UUID.prototype.tojson = function () {
return this.toString();
}
-}
+}*/
if ( typeof _threadInject != "undefined" ){
print( "fork() available!" );
@@ -667,7 +737,9 @@ if ( typeof _threadInject != "undefined" ){
"jstests/killop.js",
"jstests/run_program1.js",
"jstests/notablescan.js",
- "jstests/drop2.js"] );
+ "jstests/drop2.js",
+ "jstests/dropdb_race.js",
+ "jstests/bench_test1.js"] );
// some tests can't be run in parallel with each other
var serialTestsArr = [ "jstests/fsync.js",
@@ -903,6 +975,35 @@ printjsononeline = function(x){
print( tojsononeline( x ) );
}
+if ( typeof TestData == "undefined" ){
+ TestData = undefined
+}
+
+jsTestName = function(){
+ if( TestData ) return TestData.testName
+ return "__unknown_name__"
+}
+
+jsTestFile = function(){
+ if( TestData ) return TestData.testFile
+ return "__unknown_file__"
+}
+
+jsTestPath = function(){
+ if( TestData ) return TestData.testPath
+ return "__unknown_path__"
+}
+
+jsTestOptions = function(){
+ if( TestData ) return { noJournal : TestData.noJournal,
+ noJournalPrealloc : TestData.noJournalPrealloc }
+ return {}
+}
+
+testLog = function(x){
+ print( jsTestFile() + " - " + x )
+}
+
shellPrintHelper = function (x) {
if (typeof (x) == "undefined") {
@@ -956,7 +1057,6 @@ shellAutocomplete = function (/*prefix*/){ // outer scope function called on ini
builtinMethods[Mongo] = "find update insert remove".split(' ');
builtinMethods[BinData] = "hex base64 length subtype".split(' ');
- builtinMethods[NumberLong] = "toNumber".split(' ');
var extraGlobals = "Infinity NaN undefined null true false decodeURI decodeURIComponent encodeURI encodeURIComponent escape eval isFinite isNaN parseFloat parseInt unescape Array Boolean Date Math Number RegExp String print load gc MinKey MaxKey Mongo NumberLong ObjectId DBPointer UUID BinData Map".split(' ');
@@ -1017,7 +1117,7 @@ shellAutocomplete = function (/*prefix*/){ // outer scope function called on ini
var p = possibilities[i];
if (typeof(curObj[p]) == "undefined" && curObj != global) continue; // extraGlobals aren't in the global object
if (p.length == 0 || p.length < lastPrefix.length) continue;
- if (isPrivate(p)) continue;
+ if (lastPrefix[0] != '_' && isPrivate(p)) continue;
if (p.match(/^[0-9]+$/)) continue; // don't array number indexes
if (p.substr(0, lastPrefix.length) != lastPrefix) continue;
@@ -1046,7 +1146,7 @@ shellAutocomplete.showPrivate = false; // toggle to show (useful when working on
shellHelper = function( command , rest , shouldPrint ){
command = command.trim();
- var args = rest.trim().replace(/;$/,"").split( "\s+" );
+ var args = rest.trim().replace(/\s*;$/,"").split( "\s+" );
if ( ! shellHelper[command] )
throw "no command [" + command + "]";
@@ -1079,6 +1179,10 @@ shellHelper.it = function(){
shellHelper.show = function (what) {
assert(typeof what == "string");
+ var args = what.split( /\s+/ );
+ what = args[0]
+ args = args.splice(1)
+
if (what == "profile") {
if (db.system.profile.count() == 0) {
print("db.system.profile is empty");
@@ -1087,7 +1191,32 @@ shellHelper.show = function (what) {
}
else {
print();
- db.system.profile.find({ millis: { $gt: 0} }).sort({ $natural: -1 }).limit(5).forEach(function (x) { print("" + x.millis + "ms " + String(x.ts).substring(0, 24)); print(x.info); print("\n"); })
+ db.system.profile.find({ millis: { $gt: 0} }).sort({ $natural: -1 }).limit(5).forEach(
+ function (x) {
+ print("" + x.op + "\t" + x.ns + " " + x.millis + "ms " + String(x.ts).substring(0, 24));
+ var l = "";
+ for ( var z in x ){
+ if ( z == "op" || z == "ns" || z == "millis" || z == "ts" )
+ continue;
+
+ var val = x[z];
+ var mytype = typeof(val);
+
+ if ( mytype == "string" ||
+ mytype == "number" )
+ l += z + ":" + val + " ";
+ else if ( mytype == "object" )
+ l += z + ":" + tojson(val ) + " ";
+ else if ( mytype == "boolean" )
+ l += z + " ";
+ else
+ l += z + ":" + val + " ";
+
+ }
+ print( l );
+ print("\n");
+ }
+ )
}
return "";
}
@@ -1117,6 +1246,27 @@ shellHelper.show = function (what) {
//db.getMongo().getDBNames().sort().forEach(function (x) { print(x) });
return "";
}
+
+ if (what == "log" ) {
+ var n = "global";
+ if ( args.length > 0 )
+ n = args[0]
+
+ var res = db.adminCommand( { getLog : n } )
+ for ( var i=0; i<res.log.length; i++){
+ print( res.log[i] )
+ }
+ return ""
+ }
+
+ if (what == "logs" ) {
+ var res = db.adminCommand( { getLog : "*" } )
+ for ( var i=0; i<res.names.length; i++){
+ print( res.names[i] )
+ }
+ return ""
+ }
+
throw "don't know how to show [" + what + "]";
@@ -1314,18 +1464,40 @@ rs.slaveOk = function () { return db.getMongo().setSlaveOk(); }
rs.status = function () { return db._adminCommand("replSetGetStatus"); }
rs.isMaster = function () { return db.isMaster(); }
rs.initiate = function (c) { return db._adminCommand({ replSetInitiate: c }); }
-rs.reconfig = function (cfg) {
- cfg.version = rs.conf().version + 1;
+rs._runCmd = function (c) {
+ // after the command, catch the disconnect and reconnect if necessary
var res = null;
try {
- res = db.adminCommand({ replSetReconfig: cfg });
+ res = db.adminCommand(c);
}
catch (e) {
- print("shell got exception during reconfig: " + e);
- print("in some circumstances, the primary steps down and closes connections on a reconfig");
+ if (("" + e).indexOf("error doing query") >= 0) {
+ // closed connection. reconnect.
+ db.getLastErrorObj();
+ var o = db.getLastErrorObj();
+ if (o.ok) {
+ print("reconnected to server after rs command (which is normal)");
+ }
+ else {
+ printjson(o);
+ }
+ }
+ else {
+ print("shell got exception during repl set operation: " + e);
+ print("in some circumstances, the primary steps down and closes connections on a reconfig");
+ }
+ return "";
}
return res;
}
+rs.reconfig = function (cfg, options) {
+ cfg.version = rs.conf().version + 1;
+ cmd = { replSetReconfig: cfg };
+ for (var i in options) {
+ cmd[i] = options[i];
+ }
+ return this._runCmd(cmd);
+}
rs.add = function (hostport, arb) {
var cfg = hostport;
@@ -1345,20 +1517,13 @@ rs.add = function (hostport, arb) {
cfg.arbiterOnly = true;
}
c.members.push(cfg);
- var res = null;
- try {
- res = db.adminCommand({ replSetReconfig: c });
- }
- catch (e) {
- print("shell got exception during reconfig: " + e);
- print("in some circumstances, the primary steps down and closes connections on a reconfig");
- }
- return res;
+ return this._runCmd({ replSetReconfig: c });
}
-rs.stepDown = function (secs) { return db._adminCommand({ replSetStepDown:secs||60}); }
+rs.stepDown = function (secs) { return db._adminCommand({ replSetStepDown:(secs === undefined) ? 60:secs}); }
rs.freeze = function (secs) { return db._adminCommand({replSetFreeze:secs}); }
rs.addArb = function (hn) { return this.add(hn, true); }
rs.conf = function () { return db.getSisterDB("local").system.replset.findOne(); }
+rs.config = function () { return rs.conf(); }
rs.remove = function (hn) {
var local = db.getSisterDB("local");
@@ -1377,6 +1542,41 @@ rs.remove = function (hn) {
return "error: couldn't find "+hn+" in "+tojson(c.members);
};
+rs.debug = {};
+
+rs.debug.nullLastOpWritten = function(primary, secondary) {
+ var p = connect(primary+"/local");
+ var s = connect(secondary+"/local");
+ s.getMongo().setSlaveOk();
+
+ var secondToLast = s.oplog.rs.find().sort({$natural : -1}).limit(1).next();
+ var last = p.runCommand({findAndModify : "oplog.rs",
+ query : {ts : {$gt : secondToLast.ts}},
+ sort : {$natural : 1},
+ update : {$set : {op : "n"}}});
+
+ if (!last.value.o || !last.value.o._id) {
+ print("couldn't find an _id?");
+ }
+ else {
+ last.value.o = {_id : last.value.o._id};
+ }
+
+ print("nulling out this op:");
+ printjson(last);
+};
+
+rs.debug.getLastOpWritten = function(server) {
+ var s = db.getSisterDB("local");
+ if (server) {
+ s = connect(server+"/local");
+ }
+ s.getMongo().setSlaveOk();
+
+ return s.oplog.rs.find().sort({$natural : -1}).limit(1).next();
+};
+
+
help = shellHelper.help = function (x) {
if (x == "mr") {
print("\nSee also http://www.mongodb.org/display/DOCS/MapReduce");
@@ -1408,6 +1608,17 @@ help = shellHelper.help = function (x) {
print("\nNote: the REPL prompt only auto-reports getLastError() for the shell command line connection.\n");
return;
}
+ else if (x == "keys") {
+ print("Tab completion and command history is available at the command prompt.\n");
+ print("Some emacs keystrokes are available too:");
+ print(" Ctrl-A start of line");
+ print(" Ctrl-E end of line");
+ print(" Ctrl-K del to end of line");
+ print("\nMulti-line commands");
+ print("You can enter a multi line javascript expression. If parens, braces, etc. are not closed, you will see a new line ");
+ print("beginning with '...' characters. Type the rest of your expression. Press Ctrl-C to abort the data entry if you");
+ print("get stuck.\n");
+ }
else if (x == "misc") {
print("\tb = new BinData(subtype,base64str) create a BSON BinData value");
print("\tb.subtype() the BinData subtype (0..255)");
@@ -1416,6 +1627,10 @@ help = shellHelper.help = function (x) {
print("\tb.base64() the data as a base 64 encoded string");
print("\tb.toString()");
print();
+ print("\tb = HexData(subtype,hexstr) create a BSON BinData value from a hex string");
+ print("\tb = UUID(hexstr) create a BSON BinData value of UUID subtype");
+ print("\tb = MD5(hexstr) create a BSON BinData value of MD5 subtype");
+ print();
print("\to = new ObjectId() create a new ObjectId");
print("\to.getTimestamp() return timestamp derived from first 32 bits of the OID");
print("\to.isObjectId()");
@@ -1454,15 +1669,18 @@ help = shellHelper.help = function (x) {
print("\t" + "db.help() help on db methods");
print("\t" + "db.mycoll.help() help on collection methods");
print("\t" + "rs.help() help on replica set methods");
- print("\t" + "help connect connecting to a db help");
print("\t" + "help admin administrative help");
+ print("\t" + "help connect connecting to a db help");
+ print("\t" + "help keys key shortcuts");
print("\t" + "help misc misc things to know");
- print("\t" + "help mr mapreduce help");
+ print("\t" + "help mr mapreduce");
print();
print("\t" + "show dbs show database names");
print("\t" + "show collections show collections in current database");
print("\t" + "show users show users in current database");
print("\t" + "show profile show most recent system.profile entries with time >= 1ms");
+ print("\t" + "show logs show the accessible logger names");
+ print("\t" + "show log [name] prints out the last segment of log in memory, 'global' is default");
print("\t" + "use <db_name> set current database");
print("\t" + "db.foo.find() list objects in collection foo");
print("\t" + "db.foo.find( { a : 1 } ) list objects in foo where a == 1");
diff --git a/shell/utils_sh.js b/shell/utils_sh.js
new file mode 100644
index 0000000..5bd449b
--- /dev/null
+++ b/shell/utils_sh.js
@@ -0,0 +1,98 @@
+sh = function() { return "try sh.help();" }
+
+
+sh._checkMongos = function() {
+ var x = db.runCommand( "ismaster" );
+ if ( x.msg != "isdbgrid" )
+ throw "not connected to a mongos"
+}
+
+sh._checkFullName = function( fullName ) {
+ assert( fullName , "neeed a full name" )
+ assert( fullName.indexOf( "." ) > 0 , "name needs to be fully qualified <db>.<collection>'" )
+}
+
+sh._adminCommand = function( cmd , skipCheck ) {
+ if ( ! skipCheck ) sh._checkMongos();
+ var res = db.getSisterDB( "admin" ).runCommand( cmd );
+
+ if ( res == null || ! res.ok ) {
+ print( "command failed: " + tojson( res ) )
+ }
+
+ return res;
+}
+
+sh.help = function() {
+ print( "\tsh.addShard( host ) server:port OR setname/server:port" )
+ print( "\tsh.enableSharding(dbname) enables sharding on the database dbname" )
+ print( "\tsh.shardCollection(fullName,key,unique) shards the collection" );
+
+ print( "\tsh.splitFind(fullName,find) splits the chunk that find is in at the median" );
+ print( "\tsh.splitAt(fullName,middle) splits the chunk that middle is in at middle" );
+ print( "\tsh.moveChunk(fullName,find,to) move the chunk where 'find' is to 'to' (name of shard)");
+
+ print( "\tsh.setBalancerState( <bool on or not> ) turns the balancer on or off true=on, false=off" );
+ print( "\tsh.getBalancerState() return true if on, off if not" );
+ print( "\tsh.isBalancerRunning() return true if the balancer is running on any mongos" );
+
+ print( "\tsh.status() prints a general overview of the cluster" )
+}
+
+sh.status = function( verbose , configDB ) {
+ // TODO: move the actual commadn here
+ printShardingStatus( configDB , verbose );
+}
+
+sh.addShard = function( url ){
+ sh._adminCommand( { addShard : url } , true )
+}
+
+sh.enableSharding = function( dbname ) {
+ assert( dbname , "need a valid dbname" )
+ sh._adminCommand( { enableSharding : dbname } )
+}
+
+sh.shardCollection = function( fullName , key , unique ) {
+ sh._checkFullName( fullName )
+ assert( key , "need a key" )
+ assert( typeof( key ) == "object" , "key needs to be an object" )
+
+ var cmd = { shardCollection : fullName , key : key }
+ if ( unique )
+ cmd.unique = true;
+
+ sh._adminCommand( cmd )
+}
+
+
+sh.splitFind = function( fullName , find ) {
+ sh._checkFullName( fullName )
+ sh._adminCommand( { split : fullName , find : find } )
+}
+
+sh.splitAt = function( fullName , middle ) {
+ sh._checkFullName( fullName )
+ sh._adminCommand( { split : fullName , middle : middle } )
+}
+
+sh.moveChunk = function( fullName , find , to ) {
+ sh._checkFullName( fullName );
+ sh._adminCommand( { moveChunk : fullName , find : find , to : to } )
+}
+
+sh.setBalancerState = function( onOrNot ) {
+ db.getSisterDB( "config" ).settings.update({ _id: "balancer" }, { $set : { stopped: onOrNot ? false : true } }, true );
+}
+
+sh.getBalancerState = function() {
+ var x = db.getSisterDB( "config" ).settings.findOne({ _id: "balancer" } )
+ if ( x == null )
+ return true;
+ return ! x.stopped;
+}
+
+sh.isBalancerRunning = function() {
+ var x = db.getSisterDB( "config" ).locks.findOne( { _id : "balancer" } );
+ return x.state > 0;
+}