}
}
+static void addServerToJSON(Json::array& servers, int id, const std::shared_ptr<DownstreamState>& a)
+{
+ string status;
+ if (a->availability == DownstreamState::Availability::Up) {
+ status = "UP";
+ }
+ else if (a->availability == DownstreamState::Availability::Down) {
+ status = "DOWN";
+ }
+ else {
+ status = (a->upStatus ? "up" : "down");
+ }
+
+ Json::array pools;
+ for(const auto& p: a->pools) {
+ pools.push_back(p);
+ }
+
+ Json::object server {
+ {"id", id},
+ {"name", a->getName()},
+ {"address", a->remote.toStringWithPort()},
+ {"state", status},
+ {"qps", (double)a->queryLoad},
+ {"qpsLimit", (double)a->qps.getRate()},
+ {"outstanding", (double)a->outstanding},
+ {"reuseds", (double)a->reuseds},
+ {"weight", (double)a->weight},
+ {"order", (double)a->order},
+ {"pools", pools},
+ {"latency", (double)(a->latencyUsec/1000.0)},
+ {"queries", (double)a->queries},
+ {"responses", (double)a->responses},
+ {"sendErrors", (double)a->sendErrors},
+ {"tcpDiedSendingQuery", (double)a->tcpDiedSendingQuery},
+ {"tcpDiedReadingResponse", (double)a->tcpDiedReadingResponse},
+ {"tcpGaveUp", (double)a->tcpGaveUp},
+ {"tcpConnectTimeouts", (double)a->tcpConnectTimeouts},
+ {"tcpReadTimeouts", (double)a->tcpReadTimeouts},
+ {"tcpWriteTimeouts", (double)a->tcpWriteTimeouts},
+ {"tcpCurrentConnections", (double)a->tcpCurrentConnections},
+ {"tcpMaxConcurrentConnections", (double)a->tcpMaxConcurrentConnections},
+ {"tcpNewConnections", (double)a->tcpNewConnections},
+ {"tcpReusedConnections", (double)a->tcpReusedConnections},
+ {"tcpAvgQueriesPerConnection", (double)a->tcpAvgQueriesPerConnection},
+ {"tcpAvgConnectionDuration", (double)a->tcpAvgConnectionDuration},
+ {"dropRate", (double)a->dropRate}
+ };
+
+ /* sending a latency for a DOWN server doesn't make sense */
+ if (a->availability == DownstreamState::Availability::Down) {
+ server["latency"] = nullptr;
+ }
+
+ servers.push_back(std::move(server));
+}
+
static void handleStats(const YaHTTP::Request& req, YaHTTP::Response& resp)
{
handleCORS(req, resp);
auto localServers = g_dstates.getLocal();
int num = 0;
for (const auto& a : *localServers) {
- string status;
- if(a->availability == DownstreamState::Availability::Up)
- status = "UP";
- else if(a->availability == DownstreamState::Availability::Down)
- status = "DOWN";
- else
- status = (a->upStatus ? "up" : "down");
-
- Json::array pools;
- for(const auto& p: a->pools)
- pools.push_back(p);
-
- Json::object server{
- {"id", num++},
- {"name", a->getName()},
- {"address", a->remote.toStringWithPort()},
- {"state", status},
- {"qps", (double)a->queryLoad},
- {"qpsLimit", (double)a->qps.getRate()},
- {"outstanding", (double)a->outstanding},
- {"reuseds", (double)a->reuseds},
- {"weight", (double)a->weight},
- {"order", (double)a->order},
- {"pools", pools},
- {"latency", (double)(a->latencyUsec/1000.0)},
- {"queries", (double)a->queries},
- {"responses", (double)a->responses},
- {"sendErrors", (double)a->sendErrors},
- {"tcpDiedSendingQuery", (double)a->tcpDiedSendingQuery},
- {"tcpDiedReadingResponse", (double)a->tcpDiedReadingResponse},
- {"tcpGaveUp", (double)a->tcpGaveUp},
- {"tcpConnectTimeouts", (double)a->tcpConnectTimeouts},
- {"tcpReadTimeouts", (double)a->tcpReadTimeouts},
- {"tcpWriteTimeouts", (double)a->tcpWriteTimeouts},
- {"tcpCurrentConnections", (double)a->tcpCurrentConnections},
- {"tcpMaxConcurrentConnections", (double)a->tcpMaxConcurrentConnections},
- {"tcpNewConnections", (double)a->tcpNewConnections},
- {"tcpReusedConnections", (double)a->tcpReusedConnections},
- {"tcpAvgQueriesPerConnection", (double)a->tcpAvgQueriesPerConnection},
- {"tcpAvgConnectionDuration", (double)a->tcpAvgConnectionDuration},
- {"dropRate", (double)a->dropRate}
- };
-
- /* sending a latency for a DOWN server doesn't make sense */
- if (a->availability == DownstreamState::Availability::Down) {
- server["latency"] = nullptr;
- }
-
- servers.push_back(server);
+ addServerToJSON(servers, num++, a);
}
Json::array frontends;
resp.body = my_json.dump();
}
+static void handlePoolStats(const YaHTTP::Request& req, YaHTTP::Response& resp)
+{
+ handleCORS(req, resp);
+ const auto poolName = req.getvars.find("name");
+ if (poolName == req.getvars.end()) {
+ resp.status = 400;
+ return;
+ }
+
+ resp.status = 200;
+ Json::array doc;
+
+ auto localPools = g_pools.getLocal();
+ const auto poolIt = localPools->find(poolName->second);
+ if (poolIt == localPools->end()) {
+ resp.status = 404;
+ return;
+ }
+
+ const auto& pool = poolIt->second;
+ const auto& cache = pool->packetCache;
+ Json::object entry {
+ { "name", poolName->second },
+ { "serversCount", (double) pool->countServers(false) },
+ { "cacheSize", (double) (cache ? cache->getMaxEntries() : 0) },
+ { "cacheEntries", (double) (cache ? cache->getEntriesCount() : 0) },
+ { "cacheHits", (double) (cache ? cache->getHits() : 0) },
+ { "cacheMisses", (double) (cache ? cache->getMisses() : 0) },
+ { "cacheDeferredInserts", (double) (cache ? cache->getDeferredInserts() : 0) },
+ { "cacheDeferredLookups", (double) (cache ? cache->getDeferredLookups() : 0) },
+ { "cacheLookupCollisions", (double) (cache ? cache->getLookupCollisions() : 0) },
+ { "cacheInsertCollisions", (double) (cache ? cache->getInsertCollisions() : 0) },
+ { "cacheTTLTooShorts", (double) (cache ? cache->getTTLTooShorts() : 0) }
+ };
+
+ Json::array servers;
+ int num = 0;
+ for (const auto& a : *pool->getServers()) {
+ addServerToJSON(servers, num, a.second);
+ num++;
+ }
+
+ resp.headers["Content-Type"] = "application/json";
+ Json my_json = Json::object {
+ { "stats", entry },
+ { "servers", servers }
+ };
+
+ resp.body = my_json.dump();
+}
+
static void handleStatsOnly(const YaHTTP::Request& req, YaHTTP::Response& resp)
{
handleCORS(req, resp);
registerWebHandler("/jsonstat", handleJSONStats);
registerWebHandler("/metrics", handlePrometheus);
registerWebHandler("/api/v1/servers/localhost", handleStats);
+ registerWebHandler("/api/v1/servers/localhost/pool", handlePoolStats);
registerWebHandler("/api/v1/servers/localhost/statistics", handleStatsOnly);
registerWebHandler("/api/v1/servers/localhost/config", handleConfigDump);
registerWebHandler("/api/v1/servers/localhost/config/allow-from", handleAllowFrom);
_config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
_config_template = """
setACL({"127.0.0.1/32", "::1/128"})
- newServer{address="127.0.0.1:%s"}
+ newServer{address="127.0.0.1:%s", pool={'', 'mypool'}}
webserver("127.0.0.1:%s")
setWebserverConfig({password="%s", apiKey="%s"})
"""
for key in ['id', 'cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts']:
self.assertTrue(pool[key] >= 0)
+ def testServersLocalhostPool(self):
+ """
+ API: /api/v1/servers/localhost/pool?name=mypool
+ """
+ headers = {'x-api-key': self._webServerAPIKey}
+ url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/pool?name=mypool'
+ r = requests.get(url, headers=headers, timeout=self._webTimeout)
+ self.assertTrue(r)
+ self.assertEqual(r.status_code, 200)
+ self.assertTrue(r.json())
+ content = r.json()
+
+ self.assertIn('stats', content)
+ self.assertIn('servers', content)
+
+ for key in ['name', 'cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts']:
+ self.assertIn(key, content['stats'])
+
+ for key in ['cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts']:
+ self.assertTrue(content['stats'][key] >= 0)
+
+ for server in content['servers']:
+ for key in ['id', 'latency', 'name', 'weight', 'outstanding', 'qpsLimit',
+ 'reuseds', 'state', 'address', 'pools', 'qps', 'queries', 'order', 'sendErrors',
+ 'dropRate', 'responses', 'tcpDiedSendingQuery', 'tcpDiedReadingResponse',
+ 'tcpGaveUp', 'tcpReadTimeouts', 'tcpWriteTimeouts', 'tcpCurrentConnections',
+ 'tcpNewConnections', 'tcpReusedConnections', 'tcpAvgQueriesPerConnection',
+ 'tcpAvgConnectionDuration']:
+ self.assertIn(key, server)
+
+ for key in ['id', 'latency', 'weight', 'outstanding', 'qpsLimit', 'reuseds',
+ 'qps', 'queries', 'order']:
+ self.assertTrue(server[key] >= 0)
+
+ self.assertTrue(server['state'] in ['up', 'down', 'UP', 'DOWN'])
+
def testServersIDontExist(self):
"""
API: /api/v1/servers/idonotexist (should be 404)