/**
* 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_ptr c = 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. */
map mp;
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;
}