Save test results for circuit tests in memory and then cancel the test and send the results when the test is queried later. This way you can POST a test and then come GET the result at the appointed time.

This commit is contained in:
Adam Ierymenko 2016-01-26 12:42:44 -08:00
parent 95d28494f6
commit 9cb4bbe2b8
2 changed files with 85 additions and 68 deletions

View file

@ -555,7 +555,11 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
} }
test->timestamp = OSUtils::now(); test->timestamp = OSUtils::now();
_circuitTests[test->testId] = test;
_CircuitTestEntry &te = _circuitTests[test->testId];
te.test = test;
te.jsonResults = "";
_node->circuitTestBegin(test,&(SqliteNetworkController::_circuitTestCallback)); _node->circuitTestBegin(test,&(SqliteNetworkController::_circuitTestCallback));
return 200; return 200;
@ -1235,6 +1239,22 @@ unsigned int SqliteNetworkController::_doCPGet(
} }
} else if ((path[2] == "test")&&(path.size() >= 4)) {
std::map< uint64_t,_CircuitTestEntry >::iterator cte(_circuitTests.find(Utils::hexStrToU64(path[3].c_str())));
if (cte != _circuitTests.end()) {
responseBody = "[";
responseBody.append(cte->second.jsonResults);
responseBody.push_back(']');
responseContentType = "application/json";
_node->circuitTestEnd(cte->second.test);
::free((void *)cte->second.test);
_circuitTests.erase(cte);
} // else 404
} // else 404 } // else 404
} else { } else {
@ -1930,31 +1950,25 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c
void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report) void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report)
{ {
static Mutex circuitTestWriteLock; char tmp[65535];
SqliteNetworkController *const self = reinterpret_cast<SqliteNetworkController *>(test->ptr);
const uint64_t now = OSUtils::now(); if (!test)
SqliteNetworkController *const c = reinterpret_cast<SqliteNetworkController *>(test->ptr);
char tmp[128];
std::string reportSavePath(c->_circuitTestPath);
OSUtils::mkdir(reportSavePath);
Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.16llx",test->credentialNetworkId);
reportSavePath.append(tmp);
OSUtils::mkdir(reportSavePath);
Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.16llx_%.16llx",test->timestamp,test->testId);
reportSavePath.append(tmp);
OSUtils::mkdir(reportSavePath);
Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.16llx_%.10llx_%.10llx",now,report->upstream,report->current);
reportSavePath.append(tmp);
{
Mutex::Lock _l(circuitTestWriteLock);
FILE *f = fopen(reportSavePath.c_str(),"a");
if (!f)
return; return;
fseek(f,0,SEEK_END); if (!report)
fprintf(f,"%s{\n" return;
Mutex::Lock _l(self->_lock);
std::map< uint64_t,_CircuitTestEntry >::iterator cte(self->_circuitTests.find(test->testId));
if (cte == self->_circuitTests.end()) { // sanity check: a circuit test we didn't launch?
self->_node->circuitTestEnd(test);
::free((void *)test);
return;
}
Utils::snprintf(tmp,sizeof(tmp),
"%s{\n"
"\t\"timestamp\": %llu,"ZT_EOL_S "\t\"timestamp\": %llu,"ZT_EOL_S
"\t\"testId\": \"%.16llx\","ZT_EOL_S "\t\"testId\": \"%.16llx\","ZT_EOL_S
"\t\"upstream\": \"%.10llx\","ZT_EOL_S "\t\"upstream\": \"%.10llx\","ZT_EOL_S
@ -1975,12 +1989,12 @@ void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest
"\t\"receivedOnLocalAddress\": \"%s\","ZT_EOL_S "\t\"receivedOnLocalAddress\": \"%s\","ZT_EOL_S
"\t\"receivedFromRemoteAddress\": \"%s\""ZT_EOL_S "\t\"receivedFromRemoteAddress\": \"%s\""ZT_EOL_S
"}", "}",
((ftell(f) > 0) ? ",\n" : ""), ((cte->second.jsonResults.length() > 0) ? ",\n" : ""),
(unsigned long long)report->timestamp, (unsigned long long)report->timestamp,
(unsigned long long)test->testId, (unsigned long long)test->testId,
(unsigned long long)report->upstream, (unsigned long long)report->upstream,
(unsigned long long)report->current, (unsigned long long)report->current,
(unsigned long long)now, (unsigned long long)OSUtils::now(),
(unsigned long long)report->remoteTimestamp, (unsigned long long)report->remoteTimestamp,
(unsigned long long)report->sourcePacketId, (unsigned long long)report->sourcePacketId,
(unsigned long long)report->flags, (unsigned long long)report->flags,
@ -1995,8 +2009,8 @@ void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest
(int)report->architecture, (int)report->architecture,
reinterpret_cast<const InetAddress *>(&(report->receivedOnLocalAddress))->toString().c_str(), reinterpret_cast<const InetAddress *>(&(report->receivedOnLocalAddress))->toString().c_str(),
reinterpret_cast<const InetAddress *>(&(report->receivedFromRemoteAddress))->toString().c_str()); reinterpret_cast<const InetAddress *>(&(report->receivedFromRemoteAddress))->toString().c_str());
fclose(f);
} cte->second.jsonResults.append(tmp);
} }
} // namespace ZeroTier } // namespace ZeroTier

View file

@ -123,7 +123,7 @@ private:
std::string _circuitTestPath; std::string _circuitTestPath;
std::string _instanceId; std::string _instanceId;
// A circular buffer last log // Recent request log by device address and network ID
struct _LLEntry struct _LLEntry
{ {
_LLEntry() _LLEntry()
@ -148,12 +148,15 @@ private:
// Total requests by this address / network ID pair (also serves mod IN_MEMORY_LOG_SIZE as circular buffer ptr) // Total requests by this address / network ID pair (also serves mod IN_MEMORY_LOG_SIZE as circular buffer ptr)
uint64_t totalRequests; uint64_t totalRequests;
}; };
// Last log entries by address and network ID pair
std::map< std::pair<Address,uint64_t>,_LLEntry > _lastLog; std::map< std::pair<Address,uint64_t>,_LLEntry > _lastLog;
// Circuit tests outstanding // Circuit tests outstanding
std::map< uint64_t,ZT_CircuitTest * > _circuitTests; struct _CircuitTestEntry
{
ZT_CircuitTest *test;
std::string jsonResults;
};
std::map< uint64_t,_CircuitTestEntry > _circuitTests;
sqlite3 *_db; sqlite3 *_db;