Statistics endpoint
===================
-.. http:get:: /api/v1/servers/:server_id/statistics
+.. http:get:: /api/v1/servers/:server_id/statistics?statistic=:statistic
Query PowerDNS internal statistics.
Returns a list of :json:object:`StatisticItem` elements.
:param server_id: The name of the server
+ .. versionadded:: 4.2.0
+
+ :query statistic: If set to the name of a specific statistic, only this value is returned. If no statistic with that name exists, the response has a 422 status and an error message
+
**Example response:**
.. code-block:: json
required: true
description: The id of the server to retrieve
type: string
+ - name: statistic
+ in: query
+ required: false
+ type: string
+ description: |
+ When set to the name of a specific statistic, only this value is returned.
+ If no statistic with that name exists, the response has a 422 status and an error message.
+
responses:
'200':
description: List of Statistic Items
- $ref: '#/definitions/StatisticItem'
- $ref: '#/definitions/MapStatisticItem'
- $ref: '#/definitions/RingStatisticItem'
+ '422':
+ description: 'Returned when a non-existing statistic name has been requested. Contains an error message'
'/servers/{server_id}/search-data':
get:
if(req->method != "GET")
throw HttpMethodNotAllowedException();
+ Json::array doc;
+ string name = req->getvars["statistic"];
+ if (!name.empty()) {
+ auto stat = productServerStatisticsFetch(name);
+ if (!stat) {
+ throw ApiException("Unknown statistic name");
+ }
+
+ doc.push_back(Json::object {
+ { "type", "StatisticItem" },
+ { "name", name },
+ { "value", std::to_string(*stat) },
+ });
+
+ resp->setBody(doc);
+
+ return;
+ }
+
typedef map<string, string> stat_items_t;
stat_items_t general_stats;
productServerStatisticsFetch(general_stats);
- auto resp_qtype_stats = g_rs.getQTypeResponseCounts();
- auto resp_size_stats = g_rs.getSizeResponseCounts();
- auto resp_rcode_stats = g_rs.getRCodeResponseCounts();
-
- Json::array doc;
for(const auto& item : general_stats) {
doc.push_back(Json::object {
{ "type", "StatisticItem" },
});
}
+ auto resp_qtype_stats = g_rs.getQTypeResponseCounts();
+ auto resp_size_stats = g_rs.getSizeResponseCounts();
+ auto resp_rcode_stats = g_rs.getRCodeResponseCounts();
{
Json::array values;
for(const auto& item : resp_qtype_stats) {
// To be provided by product code.
void productServerStatisticsFetch(std::map<string,string>& out);
+boost::optional<uint64_t> productServerStatisticsFetch(const std::string& name);
#endif /* PDNS_WSAPI_HH */
out["uptime"] = std::to_string(time(0) - s_starttime);
}
+boost::optional<uint64_t> productServerStatisticsFetch(const std::string& name)
+{
+ try {
+ // ::read() calls ::exists() which throws a PDNSException when the key does not exist
+ return S.read(name);
+ }
+ catch(...) {
+ return boost::none;
+ }
+}
+
static void validateGatheredRRType(const DNSResourceRecord& rr) {
if (rr.qtype.getCode() == QType::OPT || rr.qtype.getCode() == QType::TSIG) {
throw ApiException("RRset "+rr.qname.toString()+" IN "+rr.qtype.getName()+": invalid type given");
out.swap(stats);
}
+boost::optional<uint64_t> productServerStatisticsFetch(const std::string& name)
+{
+ return getStatByName(name);
+}
+
static void apiWriteConfigFile(const string& filebasename, const string& content)
{
if (::arg()["api-config-dir"].empty()) {
self.assertIn('60', [e['name'] for e in respsize_stats])
self.assertIn('example.com/A', [e['name'] for e in queries_stats])
self.assertIn('No Error', [e['name'] for e in rcode_stats])
+
+ def test_read_one_statistic(self):
+ r = self.session.get(self.url("/api/v1/servers/localhost/statistics?statistic=uptime"))
+ self.assert_success_json(r)
+ data = r.json()
+ self.assertIn('uptime', [e['name'] for e in data])
+
+ def test_read_one_non_existent_statistic(self):
+ r = self.session.get(self.url("/api/v1/servers/localhost/statistics?statistic=uptimeAAAA"))
+ self.assertEquals(r.status_code, 422)
+ self.assertIn("Unknown statistic name", r.json()['error'])