/**
* Copyright (C) 2008 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,b
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see
\n"; int n = 0; OpTime otFirst; OpTime otLast; OpTime otEnd; while( c->more() ) { bo o = c->next(); otLast = o["ts"]._opTime(); if( otFirst.isNull() ) otFirst = otLast; say(ss, o); n++; } if( n == 0 ) { ss << rsoplog << " is empty\n"; } else { auto_ptrc = conn.query(rsoplog, Query().sort("$natural",-1), 20, 0, &fields); if( c.get() == 0 ) { ss << "couldn't query [2] " << rsoplog; return; } string x; bo o = c->next(); otEnd = o["ts"]._opTime(); while( 1 ) { stringstream z; if( o["ts"]._opTime() == otLast ) break; say(z, o); x = z.str() + x; if( !c->more() ) break; o = c->next(); } if( !x.empty() ) { ss << " \n" << x; //ss << "\n...\n\n" << x; } } ss << _table(); ss << p(time_t_to_String_short(time(0)) + " current time"); if( !otEnd.isNull() ) { ss << " ... ... ... ... ... Log length in time: "; unsigned d = otEnd.getSecs() - otFirst.getSecs(); double h = d / 3600.0; ss.precision(3); if( h < 72 ) ss << h << " hours"; else ss << h / 24.0 << " days"; ss << "
\n"; } } void ReplSetImpl::_summarizeAsHtml(stringstream& s) const { s << table(0, false); s << tr("Set name:", _name); s << tr("Majority up:", elect.aMajoritySeemsToBeUp()?"yes":"no" ); s << _table(); const char *h[] = {"Member", "id", "Up", "cctime", "Last heartbeat", "Votes", "Priority", "State", "Messages", "optime", "skew", 0 }; s << table(h); /* this is to sort the member rows by their ordinal _id, so they show up in the same order on all the different web ui's; that is less confusing for the operator. */ mapmp; string myMinValid; try { readlocktry lk("local.replset.minvalid", 300); if( lk.got() ) { BSONObj mv; if( Helpers::getSingleton("local.replset.minvalid", mv) ) { myMinValid = "minvalid:" + mv["ts"]._opTime().toString(); } } else myMinValid = "."; } catch(...) { myMinValid = "exception fetching minvalid"; } const Member *_self = this->_self; assert(_self); { stringstream s; /* self row */ s << tr() << td(_self->fullName() + " (me)") << td(_self->id()) << td("1") << //up td(ago(started)) << td("") << // last heartbeat td(ToString(_self->config().votes)) << td(ToString(_self->config().priority)) << td( stateAsHtml(box.getState()) + (_self->config().hidden?" (hidden)":"") ); s << td( _hbmsg ); stringstream q; q << "/_replSetOplog?_id=" << _self->id(); s << td( a(q.str(), myMinValid, theReplSet->lastOpTimeWritten.toString()) ); s << td(""); // skew s << _tr(); mp[_self->hbinfo().id()] = s.str(); } Member *m = head(); while( m ) { stringstream s; m->summarizeMember(s); mp[m->hbinfo().id()] = s.str(); m = m->next(); } for( map ::const_iterator i = mp.begin(); i != mp.end(); i++ ) s << i->second; s << _table(); } void fillRsLog(stringstream& s) { _rsLog->toHTML( s ); } const Member* ReplSetImpl::findById(unsigned id) const { if( _self && id == _self->id() ) return _self; for( Member *m = head(); m; m = m->next() ) if( m->id() == id ) return m; return 0; } const OpTime ReplSetImpl::lastOtherOpTime() const { OpTime closest(0,0); for( Member *m = _members.head(); m; m=m->next() ) { if (!m->hbinfo().up()) { continue; } if (m->hbinfo().opTime > closest) { closest = m->hbinfo().opTime; } } return closest; } void ReplSetImpl::_summarizeStatus(BSONObjBuilder& b) const { vector v; const Member *_self = this->_self; assert( _self ); // add self { BSONObjBuilder bb; bb.append("_id", (int) _self->id()); bb.append("name", _self->fullName()); bb.append("health", 1.0); bb.append("state", (int) box.getState().s); bb.append("stateStr", box.getState().toString()); bb.appendTimestamp("optime", lastOpTimeWritten.asDate()); bb.appendDate("optimeDate", lastOpTimeWritten.getSecs() * 1000LL); string s = _self->lhb(); if( !s.empty() ) bb.append("errmsg", s); bb.append("self", true); v.push_back(bb.obj()); } Member *m =_members.head(); while( m ) { BSONObjBuilder bb; bb.append("_id", (int) m->id()); bb.append("name", m->fullName()); double h = m->hbinfo().health; bb.append("health", h); bb.append("state", (int) m->state().s); if( h == 0 ) { // if we can't connect the state info is from the past and could be confusing to show bb.append("stateStr", "(not reachable/healthy)"); } else { bb.append("stateStr", m->state().toString()); } bb.append("uptime", (unsigned) (m->hbinfo().upSince ? (time(0)-m->hbinfo().upSince) : 0)); bb.appendTimestamp("optime", m->hbinfo().opTime.asDate()); bb.appendDate("optimeDate", m->hbinfo().opTime.getSecs() * 1000LL); bb.appendTimeT("lastHeartbeat", m->hbinfo().lastHeartbeat); bb.append("pingMs", m->hbinfo().ping); string s = m->lhb(); if( !s.empty() ) bb.append("errmsg", s); if (m->hbinfo().authIssue) { bb.append("authenticated", false); } v.push_back(bb.obj()); m = m->next(); } sort(v.begin(), v.end()); b.append("set", name()); b.appendTimeT("date", time(0)); b.append("myState", box.getState().s); const Member *syncTarget = _currentSyncTarget; if (syncTarget) { b.append("syncingTo", syncTarget->fullName()); } b.append("members", v); if( replSetBlind ) b.append("blind",true); // to avoid confusion if set...normally never set except for testing. } static struct Test : public UnitTest { void run() { HealthOptions a,b; assert( a == b ); assert( a.isDefault() ); } } test; }