summaryrefslogtreecommitdiff
path: root/src/mongo/shell/replsettest.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/shell/replsettest.js')
-rw-r--r--src/mongo/shell/replsettest.js1031
1 files changed, 1031 insertions, 0 deletions
diff --git a/src/mongo/shell/replsettest.js b/src/mongo/shell/replsettest.js
new file mode 100644
index 00000000000..c97bbc7ad04
--- /dev/null
+++ b/src/mongo/shell/replsettest.js
@@ -0,0 +1,1031 @@
+/**
+ * Sets up a replica set. To make the set running, call {@link #startSet},
+ * followed by {@link #initiate} (and optionally,
+ * {@link #awaitSecondaryNodes} to block till the set is fully operational).
+ * Note that some of the replica start up parameters are not passed here,
+ * but to the #startSet method.
+ *
+ * @param {Object} opts
+ *
+ * {
+ * name {string}: name of this replica set. Default: 'testReplSet'
+ * host {string}: name of the host machine. Hostname will be used
+ * if not specified.
+ * useHostName {boolean}: if true, use hostname of machine,
+ * otherwise use localhost
+ * nodes {number|Object|Array.<Object>}: number of replicas. Default: 0.
+ * Can also be an Object (or Array).
+ * Format for Object:
+ * {
+ * <any string>: replica member option Object. @see MongoRunner.runMongod
+ * <any string2>: and so on...
+ * }
+ *
+ * Format for Array:
+ * An array of replica member option Object. @see MongoRunner.runMongod
+ *
+ * Note: For both formats, a special boolean property 'arbiter' can be
+ * specified to denote a member is an arbiter.
+ *
+ * oplogSize {number}: Default: 40
+ * useSeedList {boolean}: Use the connection string format of this set
+ * as the replica set name (overrides the name property). Default: false
+ * bridged {boolean}: Whether to set a mongobridge between replicas.
+ * Default: false
+ * keyFile {string}
+ * shardSvr {boolean}: Default: false
+ * startPort {number}: port offset to be used for each replica. Default: 31000
+ * }
+ *
+ * Member variables:
+ * numNodes {number} - number of nodes
+ * nodes {Array.<Mongo>} - connection to replica set members
+ */
+ReplSetTest = function( opts ){
+ this.name = opts.name || "testReplSet";
+ this.useHostName = opts.useHostName == undefined ? true : opts.useHostName;
+ this.host = this.useHostName ? (opts.host || getHostName()) : 'localhost';
+ this.numNodes = opts.nodes || 0;
+ this.oplogSize = opts.oplogSize || 40;
+ this.useSeedList = opts.useSeedList || false;
+ this.bridged = opts.bridged || false;
+ this.ports = [];
+ this.keyFile = opts.keyFile
+ this.shardSvr = opts.shardSvr || false;
+
+ this.startPort = opts.startPort || 31000;
+
+ this.nodeOptions = {}
+ if( isObject( this.numNodes ) ){
+ var len = 0
+ for( var i in this.numNodes ){
+ var options = this.nodeOptions[ "n" + len ] = this.numNodes[i]
+ if( i.startsWith( "a" ) ) options.arbiter = true
+ len++
+ }
+ this.numNodes = len
+ }
+ else if( Array.isArray( this.numNodes ) ){
+ for( var i = 0; i < this.numNodes.length; i++ )
+ this.nodeOptions[ "n" + i ] = this.numNodes[i]
+ this.numNodes = this.numNodes.length
+ }
+
+ if(this.bridged) {
+ this.bridgePorts = [];
+
+ var allPorts = allocatePorts( this.numNodes * 2 , this.startPort );
+ for(var i=0; i < this.numNodes; i++) {
+ this.ports[i] = allPorts[i*2];
+ this.bridgePorts[i] = allPorts[i*2 + 1];
+ }
+
+ this.initBridges();
+ }
+ else {
+ this.ports = allocatePorts( this.numNodes , this.startPort );
+ }
+
+ this.nodes = []
+ this.initLiveNodes()
+
+ Object.extend( this, ReplSetTest.Health )
+ Object.extend( this, ReplSetTest.State )
+
+}
+
+ReplSetTest.prototype.initBridges = function() {
+ for(var i=0; i<this.ports.length; i++) {
+ startMongoProgram( "mongobridge", "--port", this.bridgePorts[i], "--dest", this.host + ":" + this.ports[i] );
+ }
+}
+
+// List of nodes as host:port strings.
+ReplSetTest.prototype.nodeList = function() {
+ var list = [];
+ for(var i=0; i<this.ports.length; i++) {
+ list.push( this.host + ":" + this.ports[i]);
+ }
+
+ return list;
+}
+
+// Here we store a reference to all reachable nodes.
+ReplSetTest.prototype.initLiveNodes = function() {
+ this.liveNodes = { master: null, slaves: [] }
+}
+
+ReplSetTest.prototype.getNodeId = function(node) {
+
+ if( node.toFixed ) return parseInt( node )
+
+ for( var i = 0; i < this.nodes.length; i++ ){
+ if( this.nodes[i] == node ) return i
+ }
+
+ if( node instanceof ObjectId ){
+ for( var i = 0; i < this.nodes.length; i++ ){
+ if( this.nodes[i].runId == node ) return i
+ }
+ }
+
+ if( node.nodeId ) return parseInt( node.nodeId )
+
+ return undefined
+
+}
+
+ReplSetTest.prototype.getPort = function( n ){
+
+ n = this.getNodeId( n )
+
+ print( "ReplSetTest n: " + n + " ports: " + tojson( this.ports ) + "\t" + this.ports[n] + " " + typeof(n) );
+ return this.ports[ n ];
+}
+
+ReplSetTest.prototype.getPath = function( n ){
+
+ if( n.host )
+ n = this.getNodeId( n )
+
+ var p = "/data/db/" + this.name + "-"+n;
+ if ( ! this._alldbpaths )
+ this._alldbpaths = [ p ];
+ else
+ this._alldbpaths.push( p );
+ return p;
+}
+
+ReplSetTest.prototype.getReplSetConfig = function() {
+ var cfg = {};
+
+ cfg['_id'] = this.name;
+ cfg.members = [];
+
+ for(i=0; i<this.ports.length; i++) {
+ member = {};
+ member['_id'] = i;
+
+ if(this.bridged)
+ var port = this.bridgePorts[i];
+ else
+ var port = this.ports[i];
+
+ member['host'] = this.host + ":" + port;
+ if( this.nodeOptions[ "n" + i ] && this.nodeOptions[ "n" + i ].arbiter )
+ member['arbiterOnly'] = true
+
+ cfg.members.push(member);
+ }
+
+ return cfg;
+}
+
+ReplSetTest.prototype.getURL = function(){
+ var hosts = [];
+
+ for(i=0; i<this.ports.length; i++) {
+
+ // Don't include this node in the replica set list
+ if(this.bridged && this.ports[i] == this.ports[n]) {
+ continue;
+ }
+
+ var port;
+ // Connect on the right port
+ if(this.bridged) {
+ port = this.bridgePorts[i];
+ }
+ else {
+ port = this.ports[i];
+ }
+
+ var str = this.host + ":" + port;
+ hosts.push(str);
+ }
+
+ return this.name + "/" + hosts.join(",");
+}
+
+ReplSetTest.prototype.getOptions = function( n , extra , putBinaryFirst ){
+
+ if ( ! extra )
+ extra = {};
+
+ if ( ! extra.oplogSize )
+ extra.oplogSize = this.oplogSize;
+
+ var a = []
+
+
+ if ( putBinaryFirst )
+ a.push( "mongod" );
+
+ if ( extra.noReplSet ) {
+ delete extra.noReplSet;
+ }
+ else {
+ a.push( "--replSet" );
+
+ if( this.useSeedList ) {
+ a.push( this.getURL() );
+ }
+ else {
+ a.push( this.name );
+ }
+ }
+
+ a.push( "--noprealloc", "--smallfiles" );
+
+ a.push( "--rest" );
+
+ a.push( "--port" );
+ a.push( this.getPort( n ) );
+
+ a.push( "--dbpath" );
+ 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" )
+ if( jsTestOptions().keyFile && !this.keyFile) {
+ a.push( "--keyFile" )
+ a.push( jsTestOptions().keyFile )
+ }
+
+ for ( var k in extra ){
+ var v = extra[k];
+ if( k in MongoRunner.logicalOptions ) continue
+ a.push( "--" + k );
+ 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;
+}
+
+ReplSetTest.prototype.startSet = function( options ) {
+
+ var nodes = [];
+ print( "ReplSetTest Starting Set" );
+
+ for( n = 0 ; n < this.ports.length; n++ ) {
+ node = this.start(n, options)
+ nodes.push(node);
+ }
+
+ this.nodes = nodes;
+ return this.nodes;
+}
+
+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
+ }
+ else {
+ this.nodes[i].setSlaveOk();
+ this.liveNodes.slaves.push(this.nodes[i]);
+ }
+
+ }
+ catch(err) {
+ print("ReplSetTest Could not call ismaster on node " + i);
+ }
+ }
+
+ return master || false;
+}
+
+ReplSetTest.awaitRSClientHosts = function( conn, host, hostOk, rs ) {
+ var hostCount = host.length;
+ if( hostCount ){
+ for( var i = 0; i < hostCount; i++ ) {
+ ReplSetTest.awaitRSClientHosts( conn, host[i], hostOk, rs );
+ }
+ 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 ( isObject( hostOk[prop] )) {
+ if ( !friendlyEqual( hostOk[prop], clientHost[prop] )){
+ propOk = false;
+ break;
+ }
+ }
+ else 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;
+ var len = slaves.length;
+
+ jsTest.attempt({context: this, timeout: 60000, desc: "Awaiting secondaries"}, function() {
+ var ready = true;
+ for(var i=0; i<len; i++) {
+ var isMaster = slaves[i].getDB("admin").runCommand({ismaster: 1});
+ var arbiter = isMaster['arbiterOnly'] == undefined ? false : isMaster['arbiterOnly'];
+ ready = ready && ( isMaster['secondary'] || arbiter );
+ }
+ return ready;
+ });
+}
+
+ReplSetTest.prototype.getMaster = function( timeout ) {
+ var tries = 0;
+ var sleepTime = 500;
+ var t = timeout || 000;
+ var master = null;
+
+ master = jsTest.attempt({context: this, timeout: 60000, desc: "Finding master"}, this.callIsMaster);
+ 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) {
+ var nextPort = allocatePorts( 1, this.startPort )[0];
+ }
+ else {
+ var nextPort = this.ports[this.ports.length-1] + 1;
+ }
+ print("ReplSetTest Next port: " + nextPort);
+ this.ports.push(nextPort);
+ printjson(this.ports);
+
+ var nextId = this.nodes.length;
+ printjson(this.nodes);
+ print("ReplSetTest nextId:" + nextId);
+ var newNode = this.start( nextId );
+
+ return newNode;
+}
+
+ReplSetTest.prototype.remove = function( nodeId ) {
+ nodeId = this.getNodeId( nodeId )
+ this.nodes.splice( nodeId, 1 );
+ this.ports.splice( nodeId, 1 );
+}
+
+ReplSetTest.prototype.initiate = function( cfg , initCmd , timeout ) {
+ var master = this.nodes[0].getDB("admin");
+ var config = cfg || this.getReplSetConfig();
+ var cmd = {};
+ var cmdKey = initCmd || 'replSetInitiate';
+ var timeout = timeout || 30000;
+ cmd[cmdKey] = config;
+ printjson(cmd);
+
+ jsTest.attempt({context:this, timeout: timeout, desc: "Initiate replica set"}, function() {
+ var result = master.runCommand(cmd);
+ printjson(result);
+ return result['ok'] == 1;
+ });
+
+ // Setup authentication if running test with authentication
+ if (jsTestOptions().keyFile && !this.keyFile) {
+ if (!this.shardSvr) {
+ master = this.getMaster();
+ jsTest.addAuth(master);
+ jsTest.authenticateNodes(this.nodes);
+ }
+ }
+}
+
+ReplSetTest.prototype.reInitiate = function() {
+ var master = this.nodes[0];
+ var c = master.getDB("local")['system.replset'].findOne();
+ var config = this.getReplSetConfig();
+ config.version = c.version + 1;
+ this.initiate( config , 'replSetReconfig' );
+}
+
+ReplSetTest.prototype.getLastOpTimeWritten = function() {
+ this.getMaster();
+ jsTest.attempt({context : this, desc : "awaiting oplog query", timeout: 30000},
+ 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);
+
+ jsTest.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 {
+ print( "ReplSetTest waiting for " + slave + " to have an oplog built." )
+ 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 ){
+ this.getMaster();
+ var res = {};
+ res.master = this.liveNodes.master.getDB( db ).runCommand( "dbhash" )
+ res.slaves = this.liveNodes.slaves.map( function(z){ return z.getDB( db ).runCommand( "dbhash" ); } )
+ return res;
+}
+
+/**
+ * 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|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. Default: false.
+ *
+ */
+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.merge({}, options), restart, wait ) ){
+ started.push( nodes[i] )
+ }
+ }
+
+ return started
+
+ }
+
+ print( "ReplSetTest n is : " + n )
+
+ defaults = { useHostName : this.useHostName,
+ oplogSize : this.oplogSize,
+ keyFile : this.keyFile,
+ port : this.getPort( n ),
+ noprealloc : "",
+ smallfiles : "",
+ rest : "",
+ replSet : this.useSeedList ? this.getURL() : this.name,
+ dbpath : "$set-$node" }
+
+ defaults = Object.merge( defaults, ReplSetTest.nodeOptions || {} )
+
+ // TODO : should we do something special if we don't currently know about this node?
+ n = this.getNodeId( n )
+
+ //
+ // Note : this replaces the binVersion of the shared startSet() options the first time
+ // through, so the full set is guaranteed to have different versions if size > 1. If using
+ // start() independently, independent version choices will be made
+ //
+ if( options && options.binVersion ){
+ options.binVersion =
+ MongoRunner.versionIterator( options.binVersion )
+ }
+
+ options = Object.merge( defaults, options )
+ options = Object.merge( options, this.nodeOptions[ "n" + n ] )
+
+ options.restart = restart
+
+ var pathOpts = { node : n, set : this.name }
+ options.pathOpts = Object.merge( options.pathOpts || {}, pathOpts )
+
+ if( tojson(options) != tojson({}) )
+ printjson(options)
+
+ // make sure to call getPath, otherwise folders wont be cleaned
+ this.getPath(n);
+
+ print("ReplSetTest " + (restart ? "(Re)" : "") + "Starting....");
+
+ var rval = this.nodes[n] = MongoRunner.runMongod( options )
+
+ if( ! rval ) return rval
+
+ // Add replica set specific attributes
+ this.nodes[n].nodeId = n
+
+ printjson( this.nodes )
+
+ wait = wait || false
+ if( ! wait.toFixed ){
+ if( wait ) wait = 0
+ else wait = -1
+ }
+
+ if( wait < 0 ) return rval
+
+ // Wait for startup
+ this.waitForHealth( rval, this.UP, wait )
+
+ return rval
+
+}
+
+
+/**
+ * Restarts a db without clearing the data directory by default. If the server is not
+ * stopped first, this function will not work.
+ *
+ * Option { startClean : true } forces clearing the data directory.
+ * Option { auth : Object } object that contains the auth details for admin credentials.
+ * Should contain the fields 'user' and 'pwd'
+ *
+ * @param {int|conn|[int|conn]} n array or single server number (0, 1, 2, ...) or conn
+ */
+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, options )
+ started = this.start( n , options , true, wait );
+
+ if (jsTestOptions().keyFile && !this.keyFile) {
+ if (started.length) {
+ // if n was an array of conns, start will return an array of connections
+ for (var i = 0; i < started.length; i++) {
+ jsTest.authenticate(started[i]);
+ }
+ } else {
+ jsTest.authenticate(started);
+ }
+ }
+ return started;
+}
+
+ReplSetTest.prototype.stopMaster = function( signal , wait, opts ) {
+ var master = this.getMaster();
+ var master_id = this.getNodeId( master );
+ return this.stop( master_id , signal , wait, opts );
+}
+
+/**
+ * Stops a particular node or nodes, specified by conn or id
+ *
+ * @param {number} n the index of the replica set member to stop
+ * @param {number} signal the signal number to use for killing
+ * @param {boolean} wait
+ * @param {Object} opts @see MongoRunner.stopMongod
+ */
+ReplSetTest.prototype.stop = function( n , signal, wait /* wait for stop */, opts ){
+
+ // 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, opts ) )
+ 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('ReplSetTest stop *** Shutting down mongod in port ' + port + ' ***');
+ var ret = MongoRunner.stopMongod( port , signal, opts );
+
+ if( ! ret || wait < 0 ) return ret
+
+ // Wait for shutdown
+ this.waitForHealth( n, this.DOWN, wait )
+
+ return true
+}
+
+/**
+ * Kill all members of this replica set.
+ *
+ * @param {number} signal The signal number to use for killing the members
+ * @param {boolean} forRestart will not cleanup data directory or teardown
+ * bridges if set to true.
+ * @param {Object} opts @see MongoRunner.stopMongod
+ */
+ReplSetTest.prototype.stopSet = function( signal , forRestart, opts ) {
+ for(var i=0; i < this.ports.length; i++) {
+ this.stop( i, signal, false, opts );
+ }
+ if ( forRestart ) { return; }
+ if ( this._alldbpaths ){
+ print("ReplSetTest stopSet deleting all dbpaths");
+ for( i=0; i<this._alldbpaths.length; i++ ){
+ resetDbpath( this._alldbpaths[i] );
+ }
+ }
+ if ( this.bridges ) {
+ var mybridgevec;
+ while (mybridgevec = this.bridges.pop()) {
+ var mybridge;
+ while (mybridge = mybridgevec.pop()) {
+ mybridge.stop();
+ }
+ }
+ }
+
+ print('ReplSetTest stopSet *** Shut down repl set - test worked ****' )
+};
+
+
+/**
+ * Waits until there is a master node
+ */
+ReplSetTest.prototype.waitForMaster = function( timeout ){
+
+ var master = undefined
+
+ jsTest.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 + " on " + node )
+ printjson( states )
+ print( "ReplSetTest waitForIndicator from node " + node )
+
+ var lastTime = null
+ var currTime = new Date().getTime()
+ var status = undefined
+
+ jsTest.attempt({context: this, timeout: timeout, desc: "waiting for state indicator " + ind + " for " + timeout + "ms" }, function() {
+
+ status = this.status()
+
+ var printStatus = false
+ 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()
+ 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
+ReplSetTest.State.ARBITER = 7
+
+/**
+ * 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( opts ) {
+ 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, opts );
+ 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, opts );
+
+ // 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();
+};