_parsePath = function() { var dbpath = ""; for( var i = 0; i < arguments.length; ++i ) if ( arguments[ i ] == "--dbpath" ) dbpath = arguments[ i + 1 ]; if ( dbpath == "" ) throw "No dbpath specified"; return dbpath; } _parsePort = function() { var port = ""; for( var i = 0; i < arguments.length; ++i ) if ( arguments[ i ] == "--port" ) port = arguments[ i + 1 ]; if ( port == "" ) throw "No port specified"; return port; } connectionURLTheSame = function( a , b ){ if ( a == b ) return true; if ( ! a || ! b ) return false; if( a.host ) return connectionURLTheSame( a.host, b ) if( b.host ) return connectionURLTheSame( a, b.host ) if( a.name ) return connectionURLTheSame( a.name, b ) if( b.name ) return connectionURLTheSame( a, b.name ) if( a.indexOf( "/" ) < 0 && b.indexOf( "/" ) < 0 ){ a = a.split( ":" ) b = b.split( ":" ) if( a.length != b.length ) return false if( a.length == 2 && a[1] != b[1] ) return false if( a[0] == "localhost" || a[0] == "127.0.0.1" ) a[0] = getHostName() if( b[0] == "localhost" || b[0] == "127.0.0.1" ) b[0] = getHostName() return a[0] == b[0] } else { a0 = a.split( "/" )[0] b0 = b.split( "/" )[0] return a0 == b0 } } 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 ]; if ( args.length == 1 && isObject( args[0] ) ){ var o = args[0]; for ( var k in o ){ if ( o.hasOwnProperty(k) ){ if ( k == "v" && isNumber( o[k] ) ){ var n = o[k]; if ( n > 0 ){ if ( n > 10 ) n = 10; var temp = "-"; while ( n-- > 0 ) temp += "v"; fullArgs.push( temp ); } } else { fullArgs.push( "--" + k ); if ( o[k] != "" ) fullArgs.push( "" + o[k] ); } } } } else { for ( var i=0; i 0 ){ if ( n > 10 ) n = 10 var temp = "-" while ( n-- > 0 ) temp += "v" fullArgs.push( temp ) } } else { if( o[k] == undefined || o[k] == null ) continue fullArgs.push( "--" + k ) if ( o[k] != "" ) fullArgs.push( "" + o[k] ) } } } else { for ( var i=0; i i + 1 && ! arr[ i + 1 ].startsWith( "-" ) ){ opts[ opt ] = arr[ i + 1 ] i++ } else{ opts[ opt ] = "" } if( opt.replace( /v/g, "" ) == "" ){ opts[ "verbose" ] = opt.length } } } return opts } MongoRunner.savedOptions = {} MongoRunner.mongoOptions = function( opts ){ // Initialize and create a copy of the opts opts = Object.merge( opts || {}, {} ) if( ! opts.restart ) opts.restart = false // RunId can come from a number of places if( isObject( opts.restart ) ){ opts.runId = opts.restart opts.restart = true } if( isObject( opts.remember ) ){ opts.runId = opts.remember opts.remember = true } else if( opts.remember == undefined ){ // Remember by default if we're restarting opts.remember = opts.restart } // If we passed in restart : or runId : if( isObject( opts.runId ) && opts.runId.runId ) opts.runId = opts.runId.runId if( opts.restart && opts.remember ) opts = Object.merge( MongoRunner.savedOptions[ opts.runId ], opts ) // Create a new runId opts.runId = opts.runId || ObjectId() // Save the port if required if( ! opts.forgetPort ) opts.port = opts.port || MongoRunner.nextOpenPort() var shouldRemember = ( ! opts.restart && ! opts.noRemember ) || ( opts.restart && opts.appendOptions ) if ( shouldRemember ){ MongoRunner.savedOptions[ opts.runId ] = Object.merge( opts, {} ) } opts.port = opts.port || MongoRunner.nextOpenPort() MongoRunner.usedPortMap[ "" + parseInt( opts.port ) ] = true opts.pathOpts = Object.merge( opts.pathOpts || {}, { port : "" + opts.port, runId : "" + opts.runId } ) return opts } MongoRunner.mongodOptions = function( opts ){ opts = MongoRunner.mongoOptions( opts ) opts.dbpath = MongoRunner.toRealDir( opts.dbpath || "$dataDir/mongod-$port", opts.pathOpts ) opts.pathOpts = Object.merge( opts.pathOpts, { dbpath : opts.dbpath } ) if( ! opts.logFile && opts.useLogFiles ){ opts.logFile = opts.dbpath + "/mongod.log" } else if( opts.logFile ){ opts.logFile = MongoRunner.toRealFile( opts.logFile, opts.pathOpts ) } if( jsTestOptions().noJournalPrealloc || opts.noJournalPrealloc ) opts.nopreallocj = "" if( jsTestOptions().noJournal || opts.noJournal ) opts.nojournal = "" if( jsTestOptions().keyFile && !opts.keyFile) { opts.keyFile = jsTestOptions().keyFile } if( opts.noReplSet ) opts.replSet = null if( opts.arbiter ) opts.oplogSize = 1 return opts } MongoRunner.mongosOptions = function( opts ){ opts = MongoRunner.mongoOptions( opts ) opts.pathOpts = Object.merge( opts.pathOpts, { configdb : opts.configdb.replace( /:|,/g, "-" ) } ) if( ! opts.logFile && opts.useLogFiles ){ opts.logFile = MongoRunner.toRealFile( "$dataDir/mongos-$configdb-$port.log", opts.pathOpts ) } else if( opts.logFile ){ opts.logFile = MongoRunner.toRealFile( opts.logFile, opts.pathOpts ) } if( jsTestOptions().keyFile && !opts.keyFile) { opts.keyFile = jsTestOptions().keyFile } return opts } MongoRunner.runMongod = function( opts ){ var useHostName = false var runId = null if( isObject( opts ) ) { opts = MongoRunner.mongodOptions( opts ) useHostName = opts.useHostName || opts.useHostname runId = opts.runId if( opts.forceLock ) removeFile( opts.dbpath + "/mongod.lock" ) if( ( opts.cleanData || opts.startClean ) || ( ! opts.restart && ! opts.noCleanData ) ){ print( "Resetting db path '" + opts.dbpath + "'" ) resetDbpath( opts.dbpath ) } opts = MongoRunner.arrOptions( "mongod", opts ) } var mongod = startMongoProgram.apply( null, opts ) mongod.commandLine = MongoRunner.arrToOpts( opts ) mongod.name = (useHostName ? getHostName() : "localhost") + ":" + mongod.commandLine.port mongod.host = mongod.name mongod.port = parseInt( mongod.commandLine.port ) mongod.runId = runId || ObjectId() mongod.savedOptions = MongoRunner.savedOptions[ mongod.runId ] return mongod } MongoRunner.runMongos = function( opts ){ var useHostName = false var runId = null if( isObject( opts ) ) { opts = MongoRunner.mongosOptions( opts ) useHostName = opts.useHostName || opts.useHostname runId = opts.runId opts = MongoRunner.arrOptions( "mongos", opts ) } var mongos = startMongoProgram.apply( null, opts ) mongos.commandLine = MongoRunner.arrToOpts( opts ) mongos.name = (useHostName ? getHostName() : "localhost") + ":" + mongos.commandLine.port mongos.host = mongos.name mongos.port = parseInt( mongos.commandLine.port ) mongos.runId = runId || ObjectId() mongos.savedOptions = MongoRunner.savedOptions[ mongos.runId ] return mongos } MongoRunner.stopMongod = function( port, signal ){ if( ! port ) { print( "Cannot stop mongo process " + port ) return } signal = signal || 15 if( port.port ) port = parseInt( port.port ) if( port instanceof ObjectId ){ var opts = MongoRunner.savedOptions( port ) if( opts ) port = parseInt( opts.port ) } var exitCode = stopMongod( parseInt( port ), parseInt( signal ) ) delete MongoRunner.usedPortMap[ "" + parseInt( port ) ] return exitCode } MongoRunner.stopMongos = MongoRunner.stopMongod MongoRunner.isStopped = function( port ){ if( ! port ) { print( "Cannot detect if process " + port + " is stopped." ) return } if( port.port ) port = parseInt( port.port ) if( port instanceof ObjectId ){ var opts = MongoRunner.savedOptions( port ) if( opts ) port = parseInt( opts.port ) } return MongoRunner.usedPortMap[ "" + parseInt( port ) ] ? false : true } __nextPort = 27000; startMongodTest = function (port, dirname, restart, extraOptions ) { if (!port) port = __nextPort++; var f = startMongodEmpty; if (restart) f = startMongodNoReset; if (!dirname) dirname = "" + port; // e.g., data/db/27000 var useHostname = false; if (extraOptions) { useHostname = extraOptions.useHostname; delete extraOptions.useHostname; } var options = { port: port, dbpath: "/data/db/" + dirname, noprealloc: "", smallfiles: "", oplogSize: "40", nohttpinterface: "" }; if( jsTestOptions().noJournal ) options["nojournal"] = "" if( jsTestOptions().noJournalPrealloc ) options["nopreallocj"] = "" if( jsTestOptions().auth ) options["auth"] = "" if( jsTestOptions().keyFile && (!extraOptions || !extraOptions['keyFile']) ) options['keyFile'] = jsTestOptions().keyFile if ( extraOptions ) Object.extend( options , extraOptions ); var conn = f.apply(null, [ options ] ); conn.name = (useHostname ? getHostName() : "localhost") + ":" + port; if (options['auth'] || options['keyFile']) { if (!this.shardsvr && !options.replSet) { jsTest.addAuth(conn); } jsTest.authenticate(conn); } return conn; } // Start a mongod instance and return a 'Mongo' object connected to it. // This function's arguments are passed as command line arguments to mongod. // The specified 'dbpath' is cleared if it exists, created if not. // var conn = startMongodEmpty("--port", 30000, "--dbpath", "asdf"); startMongodEmpty = function () { var args = createMongoArgs("mongod", arguments); var dbpath = _parsePath.apply(null, args); resetDbpath(dbpath); return startMongoProgram.apply(null, args); } startMongod = function () { print("startMongod WARNING DELETES DATA DIRECTORY THIS IS FOR TESTING ONLY"); return startMongodEmpty.apply(null, arguments); } startMongodNoReset = function(){ var args = createMongoArgs( "mongod" , arguments ); return startMongoProgram.apply( null, args ); } startMongos = function(args){ return MongoRunner.runMongos(args); } /* Start mongod or mongos and return a Mongo() object connected to there. This function's first argument is "mongod" or "mongos" program name, \ and subsequent arguments to this function are passed as command line arguments to the program. */ startMongoProgram = function(){ var port = _parsePort.apply( null, arguments ); _startMongoProgram.apply( null, arguments ); var m; assert.soon ( function() { try { m = new Mongo( "127.0.0.1:" + port ); return true; } catch( e ) { } return false; }, "unable to connect to mongo program on port " + port, 600 * 1000 ); return m; } // Start a mongo program instance. This function's first argument is the // program name, and subsequent arguments to this function are passed as // command line arguments to the program. Returns pid of the spawned program. startMongoProgramNoConnect = function() { return _startMongoProgram.apply( null, arguments ); } myPort = function() { var m = db.getMongo(); if ( m.host.match( /:/ ) ) return m.host.match( /:(.*)/ )[ 1 ]; else return 27017; } /** * otherParams can be: * * 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 otherParams = Object.merge( otherParams || {}, {} ) otherParams.extraOptions = otherParams.extraOptions || {} if( isObject( testName ) ){ var params = Object.merge( testName, {} ) testName = params.name || "test" otherParams = Object.merge( params.other || {}, {} ) otherParams.extraOptions = otherParams.extraOptions || {} numShards = params.shards || 2 verboseLevel = params.verbose || 0 numMongos = params.mongos || 1 keyFile = params.keyFile || otherParams.keyFile || otherParams.extraOptions.keyFile otherParams.nopreallocj = params.nopreallocj || otherParams.nopreallocj otherParams.rs = params.rs || ( params.other ? params.other.rs : undefined ) otherParams.chunksize = params.chunksize || ( params.other ? params.other.chunksize : undefined ) // Allow specifying options like : // { mongos : [ { noprealloc : "" } ], config : [ { smallfiles : "" } ], shards : { rs : true, d : true } } if( isObject( numShards ) ){ var len = 0 for( var i in numShards ){ otherParams[ "" + i ] = numShards[i] len++ } numShards = len } if( isObject( numMongos ) ){ var len = 0 for( var i in numMongos ){ otherParams[ "" + i ] = numMongos[i] len++ } numMongos = len } else if( Array.isArray( numMongos ) ){ for( var i = 0; i < numMongos.length; i++ ) otherParams[ "s" + i ] = numMongos[i] numMongos = numMongos.length } if( isObject( params.config ) ){ var len = 0 for( var i in params.config ){ otherParams[ "" + i ] = params.config[i] len++ } // If we're specifying explicit config options, we need separate config servers otherParams.separateConfig = true if( len == 3 ) otherParams.sync = true else otherParams.sync = false } else if( Array.isArray( params.config ) ){ for( var i = 0; i < params.config.length; i++ ) otherParams[ "c" + i ] = params.config[i] // If we're specifying explicit config options, we need separate config servers otherParams.separateConfig = true if( params.config.length == 3 ) otherParams.sync = true else otherParams.sync = false } else if( params.config ) { if( params.config == 3 ){ otherParams.separateConfig = otherParams.separateConfig || true otherParams.sync = true } } } else { // Handle legacy stuff keyFile = otherParams.extraOptions.keyFile } this._testName = testName this._otherParams = otherParams var pathOpts = this.pathOpts = { testName : testName } var hasRS = false for( var k in otherParams ){ if( k.startsWith( "rs" ) ){ hasRS = true break } } if( hasRS ){ otherParams.separateConfig = true otherParams.useHostname = otherParams.useHostname == undefined ? true : otherParams.useHostname } var localhost = otherParams.useHostname ? getHostName() : "localhost"; this._alldbpaths = [] this._connections = [] this._shardServers = this._connections this._rs = [] this._rsObjects = [] for ( var i = 0; i < numShards; i++ ) { if( otherParams.rs || otherParams["rs" + i] ){ otherParams.separateConfig = true var setName = testName + "-rs" + i; rsDefaults = { useHostname : otherParams.useHostname, noJournalPrealloc : otherParams.nopreallocj, oplogSize : 40, nodes : 3, pathOpts : Object.merge( pathOpts, { shard : i } )} rsDefaults = Object.merge( rsDefaults, otherParams.rs ) rsDefaults = Object.merge( rsDefaults, otherParams.rsOptions ) rsDefaults = Object.merge( rsDefaults, otherParams["rs" + i] ) var numReplicas = rsDefaults.nodes || otherParams.numReplicas || 3 delete rsDefaults.nodes print( "Replica set test!" ) var rs = new ReplSetTest( { name : setName , nodes : numReplicas , startPort : 31100 + ( i * 100 ), useHostName : otherParams.useHostname, keyFile : keyFile, shardSvr : true } ); this._rs[i] = { setName : setName , test : rs , nodes : rs.startSet( rsDefaults ) , url : rs.getURL() }; rs.initiate(); this["rs" + i] = rs this._rsObjects[i] = rs this._alldbpaths.push( null ) this._connections.push( null ) } else { var options = { useHostname : otherParams.useHostname, noJournalPrealloc : otherParams.nopreallocj, port : 30000 + i, pathOpts : Object.merge( pathOpts, { shard : i } ), dbpath : "$testName$shard", keyFile : keyFile } options = Object.merge( options, otherParams.shardOptions ) options = Object.merge( options, otherParams["d" + i] ) var conn = MongoRunner.runMongod( options ); this._alldbpaths.push( testName +i ) this._connections.push( conn ); this["shard" + i] = conn this["d" + i] = conn this._rs[i] = null this._rsObjects[i] = null } } // Do replication on replica sets if required for ( var i = 0; i < numShards; i++ ){ if( ! otherParams.rs && ! otherParams["rs" + i] ) continue var rs = this._rs[i].test; rs.getMaster().getDB( "admin" ).foo.save( { x : 1 } ) rs.awaitReplication(); var rsConn = new Mongo( rs.getURL() ); rsConn.name = rs.getURL(); this._connections[i] = rsConn this["shard" + i] = rsConn rsConn.rs = rs } this._configServers = [] this._configNames = [] if ( otherParams.sync && ! otherParams.separateConfig && numShards < 3 ) throw "if you want sync, you need at least 3 servers"; for ( var i = 0; i < ( otherParams.sync ? 3 : 1 ) ; i++ ) { var conn = null if( otherParams.separateConfig ){ var options = { useHostname : otherParams.useHostname, noJournalPrealloc : otherParams.nopreallocj, port : 40000 + i, pathOpts : Object.merge( pathOpts, { config : i } ), dbpath : "$testName-config$config", keyFile : keyFile } options = Object.merge( options, otherParams.configOptions ) options = Object.merge( options, otherParams["c" + i] ) var conn = MongoRunner.runMongod( options ) // TODO: Needed? this._alldbpaths.push( testName + "-config" + i ) } else{ conn = this["shard" + i] } this._configServers.push( conn ); this._configNames.push( conn.name ) this["config" + i] = conn this["c" + i] = conn } printjson( this._configDB = this._configNames.join( "," ) ) this._configConnection = new Mongo( this._configDB ) if ( ! otherParams.noChunkSize ) { this._configConnection.getDB( "config" ).settings.insert( { _id : "chunksize" , value : otherParams.chunksize || otherParams.chunkSize || 50 } ) } print( "ShardingTest " + this._testName + " :\n" + tojson( { config : this._configDB, shards : this._connections } ) ); this._mongos = [] this._mongoses = this._mongos for ( var i = 0; i < ( ( numMongos == 0 ? -1 : numMongos ) || 1 ); i++ ){ var options = { useHostname : otherParams.useHostname, port : 31000 - i - 1, pathOpts : Object.merge( pathOpts, { mongos : i } ), configdb : this._configDB, verbose : verboseLevel || 0, keyFile : keyFile } options = Object.merge( options, otherParams.mongosOptions ) options = Object.merge( options, otherParams.extraOptions ) options = Object.merge( options, otherParams["s" + i] ) var conn = MongoRunner.runMongos( options ) this._mongos.push( conn ); if ( i == 0 ) this.s = conn this["s" + i] = conn } var admin = this.admin = this.s.getDB( "admin" ); this.config = this.s.getDB( "config" ); if ( ! otherParams.manualAddShard ){ this._shardNames = [] var shardNames = this._shardNames this._connections.forEach( function(z){ var n = z.name; if ( ! n ){ n = z.host; if ( ! n ) n = z; } print( "ShardingTest " + this._testName + " going to add shard : " + n ) x = admin.runCommand( { addshard : n } ); printjson( x ) shardNames.push( x.shardAdded ) z.shardName = x.shardAdded } ); } if (jsTestOptions().keyFile && !keyFile) { jsTest.addAuth(this._mongos[0]); jsTest.authenticateNodes(this._connections); jsTest.authenticateNodes(this._configServers); jsTest.authenticateNodes(this._mongos); } } ShardingTest.prototype.getRSEntry = function( setName ){ for ( var i=0; i 0 ) rsName = name.substring( 0 , name.indexOf( "/" ) ); for ( var i=0; i " + tojsononeline( r.max ); } ShardingTest.prototype.printChangeLog = function(){ var s = this; this.config.changelog.find().forEach( function(z){ var msg = z.server + "\t" + z.time + "\t" + z.what; for ( i=z.what.length; i<15; i++ ) msg += " "; msg += " " + z.ns + "\t"; if ( z.what == "split" ){ msg += s._rangeToString( z.details.before ) + " -->> (" + s._rangeToString( z.details.left ) + "),(" + s._rangeToString( z.details.right ) + ")"; } else if (z.what == "multi-split" ){ msg += s._rangeToString( z.details.before ) + " -->> (" + z.details.number + "/" + z.details.of + " " + s._rangeToString( z.details.chunk ) + ")"; } else { msg += tojsononeline( z.details ); } print( "ShardingTest " + msg ) } ); } ShardingTest.prototype.getChunksString = function( ns ){ var q = {} if ( ns ) q.ns = ns; var s = ""; this.config.chunks.find( q ).sort( { ns : 1 , min : 1 } ).forEach( function(z){ s += " " + z._id + "\t" + z.lastmod.t + "|" + z.lastmod.i + "\t" + tojson(z.min) + " -> " + tojson(z.max) + " " + z.shard + " " + z.ns + "\n"; } ); return s; } ShardingTest.prototype.printChunks = function( ns ){ print( "ShardingTest " + this.getChunksString( ns ) ); } ShardingTest.prototype.printShardingStatus = function(){ printShardingStatus( this.config ); } ShardingTest.prototype.printCollectionInfo = function( ns , msg ){ var out = ""; if ( msg ) out += msg + "\n"; out += "sharding collection info: " + ns + "\n"; for ( var i=0; i> " + tojson( chunk.max ) + " on : " + chunk.shard + " " + tojson( chunk.lastmod ) + " " + ( chunk.jumbo ? "jumbo " : "" ) ); } ); } else { output( "\t\t\ttoo many chunks to print, use verbose if you want to force print" ); } } } ) } } ); print( raw ); } printShardingSizes = function(){ configDB = db.getSisterDB('config') var version = configDB.getCollection( "version" ).findOne(); if ( version == null ){ print( "printShardingSizes : not a shard db!" ); return; } var raw = ""; var output = function(s){ raw += s + "\n"; } output( "--- Sharding Status --- " ); output( " sharding version: " + tojson( configDB.getCollection( "version" ).findOne() ) ); output( " shards:" ); var shards = {}; configDB.shards.find().forEach( function(z){ shards[z._id] = new Mongo(z.host); output( " " + tojson(z) ); } ); var saveDB = db; output( " databases:" ); configDB.databases.find().sort( { name : 1 } ).forEach( function(db){ output( "\t" + tojson(db,"",true) ); if (db.partitioned){ configDB.collections.find( { _id : new RegExp( "^" + db._id + "\." ) } ).sort( { _id : 1 } ).forEach( function( coll ){ output("\t\t" + coll._id + " chunks:"); configDB.chunks.find( { "ns" : coll._id } ).sort( { min : 1 } ).forEach( function(chunk){ var mydb = shards[chunk.shard].getDB(db._id) var out = mydb.runCommand({dataSize: coll._id, keyPattern: coll.key, min: chunk.min, max: chunk.max }); delete out.millis; delete out.ok; output( "\t\t\t" + tojson( chunk.min ) + " -->> " + tojson( chunk.max ) + " on : " + chunk.shard + " " + tojson( out ) ); } ); } ) } } ); print( raw ); } ShardingTest.prototype.sync = function(){ this.adminCommand( "connpoolsync" ); } ShardingTest.prototype.onNumShards = function( collName , dbName ){ this.sync(); // we should sync since we're going directly to mongod here dbName = dbName || "test"; var num=0; for ( var i=0; i 0 ) num++; return num; } ShardingTest.prototype.shardCounts = function( collName , dbName ){ this.sync(); // we should sync since we're going directly to mongod here dbName = dbName || "test"; var counts = {} for ( var i=0; i max ) max = c[s]; } print( "ShardingTest input: " + tojson( c ) + " min: " + min + " max: " + max ); return max - min; } ShardingTest.prototype.getShard = function( coll, query, includeEmpty ){ var shards = this.getShards( coll, query, includeEmpty ) 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, includeEmpty ){ 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( includeEmpty || ( 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] , 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 != false ? ( split || key ) : split ) move = ( split != false && move != false ? ( move || split ) : false ) if( collName.getDB ) dbName = "" + collName.getDB() else dbName = dbName || "test"; var c = dbName + "." + collName; if( collName.getDB ) c = "" + collName 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 ) } if( split == false ) return result = this.s.adminCommand( { split : c , middle : split } ); if( ! result.ok ){ printjson( result ) assert( false ) } if( move == false ) return 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 ) } } ShardingTest.prototype.stopBalancer = function( timeout, interval ) { this.setBalancer( false ) if( typeof db == "undefined" ) db = undefined var oldDB = db db = this.config sh.waitForBalancer( false, timeout, interval ) db = oldDB } ShardingTest.prototype.startBalancer = function( timeout, interval ) { this.setBalancer( true ) if( typeof db == "undefined" ) db = undefined var oldDB = db db = this.config sh.waitForBalancer( true, timeout, interval ) db = oldDB } /** * Run a mongod process. * * After initializing a MongodRunner, you must call start() on it. * @param {int} port port to run db on, use allocatePorts(num) to requision * @param {string} dbpath path to use * @param {boolean} peer pass in false (DEPRECATED, was used for replica pair host) * @param {boolean} arbiter pass in false (DEPRECATED, was used for replica pair host) * @param {array} extraArgs other arguments for the command line * @param {object} options other options include no_bind to not bind_ip to 127.0.0.1 * (necessary for replica set testing) */ MongodRunner = function( port, dbpath, peer, arbiter, extraArgs, options ) { this.port_ = port; this.dbpath_ = dbpath; this.peer_ = peer; this.arbiter_ = arbiter; this.extraArgs_ = extraArgs; this.options_ = options ? options : {}; }; /** * Start this mongod process. * * @param {boolean} reuseData If the data directory should be left intact (default is to wipe it) */ MongodRunner.prototype.start = function( reuseData ) { var args = []; if ( reuseData ) { args.push( "mongod" ); } args.push( "--port" ); args.push( this.port_ ); args.push( "--dbpath" ); args.push( this.dbpath_ ); args.push( "--nohttpinterface" ); args.push( "--noprealloc" ); args.push( "--smallfiles" ); if (!this.options_.no_bind) { args.push( "--bind_ip" ); args.push( "127.0.0.1" ); } if ( this.extraArgs_ ) { args = args.concat( this.extraArgs_ ); } removeFile( this.dbpath_ + "/mongod.lock" ); if ( reuseData ) { return startMongoProgram.apply( null, args ); } else { return startMongod.apply( null, args ); } } MongodRunner.prototype.port = function() { return this.port_; } MongodRunner.prototype.toString = function() { return [ this.port_, this.dbpath_, this.peer_, this.arbiter_ ].toString(); } ToolTest = function( name ){ this.name = name; this.port = allocatePorts(1)[0]; this.baseName = "jstests_tool_" + name; this.root = "/data/db/" + this.baseName; this.dbpath = this.root + "/"; this.ext = this.root + "_external/"; this.extFile = this.root + "_external/a"; resetDbpath( this.dbpath ); } ToolTest.prototype.startDB = function( coll ){ assert( ! this.m , "db already running" ); this.m = startMongoProgram( "mongod" , "--port", this.port , "--dbpath" , this.dbpath , "--nohttpinterface", "--noprealloc" , "--smallfiles" , "--bind_ip", "127.0.0.1" ); this.db = this.m.getDB( this.baseName ); if ( coll ) return this.db.getCollection( coll ); return this.db; } ToolTest.prototype.stop = function(){ if ( ! this.m ) return; stopMongod( this.port ); this.m = null; this.db = null; print('*** ' + this.name + " completed successfully ***"); } ToolTest.prototype.runTool = function(){ var a = [ "mongo" + arguments[0] ]; var hasdbpath = false; for ( var i=1; i lastTime ){ if( lastTime == null ) print( "ReplSetTest waitForIndicator Initial status ( timeout : " + timeout + " ) :" ) printjson( status ) lastTime = new Date().getTime() printStatus = true } if (typeof status.members == 'undefined') { return false; } for( var i = 0; i < status.members.length; i++ ){ if( printStatus ) print( "Status for : " + status.members[i].name + ", checking " + node.host + "/" + node.name ) if( status.members[i].name == node.host || status.members[i].name == node.name ){ for( var j = 0; j < states.length; j++ ){ if( printStatus ) print( "Status " + " : " + status.members[i][ind] + " target state : " + states[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; i1, 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; };