named.node.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. 'use strict';
  2. // SPDX-License-Identifier: GPL-3.0-or-later
  3. // collect statistics from bind (named) v9.10+
  4. //
  5. // bind statistics documentation at:
  6. // http://jpmens.net/2013/03/18/json-in-bind-9-s-statistics-server/
  7. // https://ftp.isc.org/isc/bind/9.10.3/doc/arm/Bv9ARM.ch06.html#statistics
  8. // example configuration in /etc/netdata/node.d/named.conf
  9. // the module supports auto-detection if bind is running at localhost
  10. /*
  11. {
  12. "enable_autodetect": true,
  13. "update_every": 5,
  14. "servers": [
  15. {
  16. "name": "bind1",
  17. "url": "http://127.0.0.1:8888/json/v1/server",
  18. "update_every": 1
  19. },
  20. {
  21. "name": "bind2",
  22. "url": "http://10.0.0.1:8888/xml/v3/server",
  23. "update_every": 2
  24. }
  25. ]
  26. }
  27. */
  28. // the following is the bind named.conf configuration required
  29. /*
  30. statistics-channels {
  31. inet 127.0.0.1 port 8888 allow { 127.0.0.1; };
  32. };
  33. */
  34. require('url');
  35. require('http');
  36. var XML = require('pixl-xml');
  37. var netdata = require('netdata');
  38. if(netdata.options.DEBUG === true) netdata.debug('loaded', __filename, 'plugin');
  39. var named = {
  40. name: __filename,
  41. enable_autodetect: true,
  42. update_every: 1,
  43. base_priority: 60000,
  44. charts: {},
  45. chartFromMembersCreate: function(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor) {
  46. var chart = {
  47. id: id, // the unique id of the chart
  48. name: '', // the unique name of the chart
  49. title: service.name + ' ' + title_suffix, // the title of the chart
  50. units: units, // the units of the chart dimensions
  51. family: family, // the family of the chart
  52. context: context, // the context of the chart
  53. type: type, // the type of the chart
  54. priority: priority, // the priority relative to others in the same family
  55. update_every: service.update_every, // the expected update frequency of the chart
  56. dimensions: {}
  57. };
  58. var found = 0;
  59. var dims = Object.keys(obj);
  60. var len = dims.length;
  61. for(var i = 0; i < len ;i++) {
  62. var x = dims[i];
  63. if(typeof(obj[x]) !== 'undefined' && obj[x] !== 0) {
  64. found++;
  65. chart.dimensions[x] = {
  66. id: x, // the unique id of the dimension
  67. name: x, // the name of the dimension
  68. algorithm: algorithm, // the id of the netdata algorithm
  69. multiplier: multiplier, // the multiplier
  70. divisor: divisor, // the divisor
  71. hidden: false // is hidden (boolean)
  72. };
  73. }
  74. }
  75. if(!found)
  76. return null;
  77. chart = service.chart(id, chart);
  78. this.charts[id] = chart;
  79. return chart;
  80. },
  81. chartFromMembers: function(service, obj, id_suffix, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor) {
  82. var id = 'named_' + service.name + '.' + id_suffix;
  83. var chart = this.charts[id];
  84. var dims, len, x, i;
  85. if(typeof chart === 'undefined') {
  86. chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor);
  87. if(chart === null) return false;
  88. }
  89. else {
  90. // check if we need to re-generate the chart
  91. dims = Object.keys(obj);
  92. len = dims.length;
  93. for(i = 0; i < len ;i++) {
  94. x = dims[i];
  95. if(typeof(chart.dimensions[x]) === 'undefined') {
  96. chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor);
  97. if(chart === null) return false;
  98. break;
  99. }
  100. }
  101. }
  102. service.begin(chart);
  103. var found = 0;
  104. dims = Object.keys(obj);
  105. len = dims.length;
  106. for(i = 0; i < len ;i++) {
  107. x = dims[i];
  108. if(typeof(chart.dimensions[x]) !== 'undefined') {
  109. found++;
  110. service.set(x, obj[x]);
  111. }
  112. }
  113. service.end();
  114. return (found > 0);
  115. },
  116. // an index to map values to different charts
  117. lookups: {
  118. nsstats: {},
  119. resolver_stats: {},
  120. numfetch: {}
  121. },
  122. // transform the XML response of bind
  123. // to the JSON response of bind
  124. xml2js: function(service, data_xml) {
  125. var d = XML.parse(data_xml);
  126. if(d === null) return null;
  127. var a, aa, alen, alen2;
  128. var data = {};
  129. var len = d.server.counters.length;
  130. while(len--) {
  131. a = d.server.counters[len];
  132. if(typeof a.counter === 'undefined') continue;
  133. if(a.type === 'opcode') a.type = 'opcodes';
  134. else if(a.type === 'qtype') a.type = 'qtypes';
  135. else if(a.type === 'nsstat') a.type = 'nsstats';
  136. aa = data[a.type] = {};
  137. alen = 0;
  138. alen2 = a.counter.length;
  139. while(alen < alen2) {
  140. aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data, 10);
  141. alen++;
  142. }
  143. }
  144. data.views = {};
  145. var vlen = d.views.view.length;
  146. while(vlen--) {
  147. var vname = d.views.view[vlen].name;
  148. data.views[vname] = { resolver: {} };
  149. len = d.views.view[vlen].counters.length;
  150. while(len--) {
  151. a = d.views.view[vlen].counters[len];
  152. if(typeof a.counter === 'undefined') continue;
  153. if(a.type === 'resstats') a.type = 'stats';
  154. else if(a.type === 'resqtype') a.type = 'qtypes';
  155. else if(a.type === 'adbstat') a.type = 'adb';
  156. aa = data.views[vname].resolver[a.type] = {};
  157. alen = 0;
  158. alen2 = a.counter.length;
  159. while(alen < alen2) {
  160. aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data, 10);
  161. alen++;
  162. }
  163. }
  164. }
  165. return data;
  166. },
  167. processResponse: function(service, data) {
  168. if(data !== null) {
  169. var r, x, look, id, chart, keys, len;
  170. // parse XML or JSON
  171. // pepending on the URL given
  172. if(service.request.path.match(/^\/xml/) !== null)
  173. r = named.xml2js(service, data);
  174. else
  175. r = JSON.parse(data);
  176. if(typeof r === 'undefined' || r === null) {
  177. service.error("Cannot parse these data: " + data.toString());
  178. return;
  179. }
  180. if(service.added !== true)
  181. service.commit();
  182. if(typeof r.nsstats !== 'undefined') {
  183. // we split the nsstats object to several others
  184. var global_requests = {}, global_requests_enable = false;
  185. var global_failures = {}, global_failures_enable = false;
  186. var global_failures_detail = {}, global_failures_detail_enable = false;
  187. var global_updates = {}, global_updates_enable = false;
  188. var protocol_queries = {}, protocol_queries_enable = false;
  189. var global_queries = {}, global_queries_enable = false;
  190. var global_queries_success = {}, global_queries_success_enable = false;
  191. var default_enable = false;
  192. var RecursClients = 0;
  193. // RecursClients is an absolute value
  194. if(typeof r.nsstats['RecursClients'] !== 'undefined') {
  195. RecursClients = r.nsstats['RecursClients'];
  196. delete r.nsstats['RecursClients'];
  197. }
  198. keys = Object.keys(r.nsstats);
  199. len = keys.length;
  200. while(len--) {
  201. x = keys[len];
  202. // we maintain an index of the values found
  203. // mapping them to objects splitted
  204. look = named.lookups.nsstats[x];
  205. if(typeof look === 'undefined') {
  206. // a new value, not found in the index
  207. // index it:
  208. if(x === 'Requestv4') {
  209. named.lookups.nsstats[x] = {
  210. name: 'IPv4',
  211. type: 'global_requests'
  212. };
  213. }
  214. else if(x === 'Requestv6') {
  215. named.lookups.nsstats[x] = {
  216. name: 'IPv6',
  217. type: 'global_requests'
  218. };
  219. }
  220. else if(x === 'QryFailure') {
  221. named.lookups.nsstats[x] = {
  222. name: 'failures',
  223. type: 'global_failures'
  224. };
  225. }
  226. else if(x === 'QryUDP') {
  227. named.lookups.nsstats[x] = {
  228. name: 'UDP',
  229. type: 'protocol_queries'
  230. };
  231. }
  232. else if(x === 'QryTCP') {
  233. named.lookups.nsstats[x] = {
  234. name: 'TCP',
  235. type: 'protocol_queries'
  236. };
  237. }
  238. else if(x === 'QrySuccess') {
  239. named.lookups.nsstats[x] = {
  240. name: 'queries',
  241. type: 'global_queries_success'
  242. };
  243. }
  244. else if(x.match(/QryRej$/) !== null) {
  245. named.lookups.nsstats[x] = {
  246. name: x,
  247. type: 'global_failures_detail'
  248. };
  249. }
  250. else if(x.match(/^Qry/) !== null) {
  251. named.lookups.nsstats[x] = {
  252. name: x,
  253. type: 'global_queries'
  254. };
  255. }
  256. else if(x.match(/^Update/) !== null) {
  257. named.lookups.nsstats[x] = {
  258. name: x,
  259. type: 'global_updates'
  260. };
  261. }
  262. else {
  263. // values not mapped, will remain
  264. // in the default map
  265. named.lookups.nsstats[x] = {
  266. name: x,
  267. type: 'default'
  268. };
  269. }
  270. look = named.lookups.nsstats[x];
  271. // netdata.error('lookup nsstats value: ' + x + ' >>> ' + named.lookups.nsstats[x].type);
  272. }
  273. switch(look.type) {
  274. case 'global_requests': global_requests[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_requests_enable = true; break;
  275. case 'global_queries': global_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_enable = true; break;
  276. case 'global_queries_success': global_queries_success[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_success_enable = true; break;
  277. case 'global_updates': global_updates[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_updates_enable = true; break;
  278. case 'protocol_queries': protocol_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; protocol_queries_enable = true; break;
  279. case 'global_failures': global_failures[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_enable = true; break;
  280. case 'global_failures_detail': global_failures_detail[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_detail_enable = true; break;
  281. default: default_enable = true; break;
  282. }
  283. }
  284. if(global_requests_enable === true)
  285. service.module.chartFromMembers(service, global_requests, 'received_requests', 'Bind, Global Received Requests by IP version', 'requests/s', 'requests', 'named.requests', netdata.chartTypes.stacked, named.base_priority + 1, netdata.chartAlgorithms.incremental, 1, 1);
  286. if(global_queries_success_enable === true)
  287. service.module.chartFromMembers(service, global_queries_success, 'global_queries_success', 'Bind, Global Successful Queries', 'queries/s', 'queries', 'named.queries_succcess', netdata.chartTypes.line, named.base_priority + 2, netdata.chartAlgorithms.incremental, 1, 1);
  288. if(protocol_queries_enable === true)
  289. service.module.chartFromMembers(service, protocol_queries, 'protocols_queries', 'Bind, Global Queries by IP Protocol', 'queries/s', 'queries', 'named.protocol_queries', netdata.chartTypes.stacked, named.base_priority + 3, netdata.chartAlgorithms.incremental, 1, 1);
  290. if(global_queries_enable === true)
  291. service.module.chartFromMembers(service, global_queries, 'global_queries', 'Bind, Global Queries Analysis', 'queries/s', 'queries', 'named.global_queries', netdata.chartTypes.stacked, named.base_priority + 4, netdata.chartAlgorithms.incremental, 1, 1);
  292. if(global_updates_enable === true)
  293. service.module.chartFromMembers(service, global_updates, 'received_updates', 'Bind, Global Received Updates', 'updates/s', 'updates', 'named.global_updates', netdata.chartTypes.stacked, named.base_priority + 5, netdata.chartAlgorithms.incremental, 1, 1);
  294. if(global_failures_enable === true)
  295. service.module.chartFromMembers(service, global_failures, 'query_failures', 'Bind, Global Query Failures', 'failures/s', 'failures', 'named.global_failures', netdata.chartTypes.line, named.base_priority + 6, netdata.chartAlgorithms.incremental, 1, 1);
  296. if(global_failures_detail_enable === true)
  297. service.module.chartFromMembers(service, global_failures_detail, 'query_failures_detail', 'Bind, Global Query Failures Analysis', 'failures/s', 'failures', 'named.global_failures_detail', netdata.chartTypes.stacked, named.base_priority + 7, netdata.chartAlgorithms.incremental, 1, 1);
  298. if(default_enable === true)
  299. service.module.chartFromMembers(service, r.nsstats, 'nsstats', 'Bind, Other Global Server Statistics', 'operations/s', 'other', 'named.nsstats', netdata.chartTypes.line, named.base_priority + 8, netdata.chartAlgorithms.incremental, 1, 1);
  300. // RecursClients chart
  301. id = 'named_' + service.name + '.recursive_clients';
  302. chart = named.charts[id];
  303. if(typeof chart === 'undefined') {
  304. chart = {
  305. id: id, // the unique id of the chart
  306. name: '', // the unique name of the chart
  307. title: service.name + ' Bind, Current Recursive Clients', // the title of the chart
  308. units: 'clients', // the units of the chart dimensions
  309. family: 'clients', // the family of the chart
  310. context: 'named.recursive_clients', // the context of the chart
  311. type: netdata.chartTypes.line, // the type of the chart
  312. priority: named.base_priority + 1, // the priority relative to others in the same family
  313. update_every: service.update_every, // the expected update frequency of the chart
  314. dimensions: {
  315. 'clients': {
  316. id: 'clients', // the unique id of the dimension
  317. name: '', // the name of the dimension
  318. algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
  319. multiplier: 1, // the multiplier
  320. divisor: 1, // the divisor
  321. hidden: false // is hidden (boolean)
  322. }
  323. }
  324. };
  325. chart = service.chart(id, chart);
  326. named.charts[id] = chart;
  327. }
  328. service.begin(chart);
  329. service.set('clients', RecursClients);
  330. service.end();
  331. }
  332. if(typeof r.opcodes !== 'undefined')
  333. service.module.chartFromMembers(service, r.opcodes, 'in_opcodes', 'Bind, Global Incoming Requests by OpCode', 'requests/s', 'requests', 'named.in_opcodes', netdata.chartTypes.stacked, named.base_priority + 9, netdata.chartAlgorithms.incremental, 1, 1);
  334. if(typeof r.qtypes !== 'undefined')
  335. service.module.chartFromMembers(service, r.qtypes, 'in_qtypes', 'Bind, Global Incoming Requests by Query Type', 'requests/s', 'requests', 'named.in_qtypes', netdata.chartTypes.stacked, named.base_priority + 10, netdata.chartAlgorithms.incremental, 1, 1);
  336. if(typeof r.sockstats !== 'undefined')
  337. service.module.chartFromMembers(service, r.sockstats, 'in_sockstats', 'Bind, Global Socket Statistics', 'operations/s', 'sockets', 'named.in_sockstats', netdata.chartTypes.line, named.base_priority + 11, netdata.chartAlgorithms.incremental, 1, 1);
  338. if(typeof r.views !== 'undefined') {
  339. keys = Object.keys(r.views);
  340. len = keys.length;
  341. while(len--) {
  342. x = keys[len];
  343. var resolver = r.views[x].resolver;
  344. if(typeof resolver !== 'undefined') {
  345. if(typeof resolver.stats !== 'undefined') {
  346. var NumFetch = 0;
  347. var key = service.name + '.' + x;
  348. var rtt = {}, rtt_enable = false;
  349. default_enable = false;
  350. // NumFetch is an absolute value
  351. if(typeof resolver.stats['NumFetch'] !== 'undefined') {
  352. named.lookups.numfetch[key] = true;
  353. NumFetch = resolver.stats['NumFetch'];
  354. delete resolver.stats['NumFetch'];
  355. }
  356. if(typeof resolver.stats['BucketSize'] !== 'undefined') {
  357. delete resolver.stats['BucketSize'];
  358. }
  359. // split the QryRTT* from the main chart
  360. var ykeys = Object.keys(resolver.stats);
  361. var ylen = ykeys.length;
  362. while(ylen--) {
  363. var y = ykeys[ylen];
  364. // we maintain an index of the values found
  365. // mapping them to objects splitted
  366. look = named.lookups.resolver_stats[y];
  367. if(typeof look === 'undefined') {
  368. if(y.match(/^QryRTT/) !== null) {
  369. named.lookups.resolver_stats[y] = {
  370. name: y,
  371. type: 'rtt'
  372. };
  373. }
  374. else {
  375. named.lookups.resolver_stats[y] = {
  376. name: y,
  377. type: 'default'
  378. };
  379. }
  380. look = named.lookups.resolver_stats[y];
  381. // netdata.error('lookup resolver stats value: ' + y + ' >>> ' + look.type);
  382. }
  383. switch(look.type) {
  384. case 'rtt': rtt[look.name] = resolver.stats[y]; delete resolver.stats[y]; rtt_enable = true; break;
  385. default: default_enable = true; break;
  386. }
  387. }
  388. if(rtt_enable)
  389. service.module.chartFromMembers(service, rtt, 'view_resolver_rtt_' + x, 'Bind, ' + x + ' View, Resolver Round Trip Timings', 'queries/s', 'view_' + x, 'named.resolver_rtt', netdata.chartTypes.stacked, named.base_priority + 12, netdata.chartAlgorithms.incremental, 1, 1);
  390. if(default_enable)
  391. service.module.chartFromMembers(service, resolver.stats, 'view_resolver_stats_' + x, 'Bind, ' + x + ' View, Resolver Statistics', 'operations/s', 'view_' + x, 'named.resolver_stats', netdata.chartTypes.line, named.base_priority + 13, netdata.chartAlgorithms.incremental, 1, 1);
  392. // NumFetch chart
  393. if(typeof named.lookups.numfetch[key] !== 'undefined') {
  394. id = 'named_' + service.name + '.view_resolver_numfetch_' + x;
  395. chart = named.charts[id];
  396. if(typeof chart === 'undefined') {
  397. chart = {
  398. id: id, // the unique id of the chart
  399. name: '', // the unique name of the chart
  400. title: service.name + ' Bind, ' + x + ' View, Resolver Active Queries', // the title of the chart
  401. units: 'queries', // the units of the chart dimensions
  402. family: 'view_' + x, // the family of the chart
  403. context: 'named.resolver_active_queries', // the context of the chart
  404. type: netdata.chartTypes.line, // the type of the chart
  405. priority: named.base_priority + 1001, // the priority relative to others in the same family
  406. update_every: service.update_every, // the expected update frequency of the chart
  407. dimensions: {
  408. 'queries': {
  409. id: 'queries', // the unique id of the dimension
  410. name: '', // the name of the dimension
  411. algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
  412. multiplier: 1, // the multiplier
  413. divisor: 1, // the divisor
  414. hidden: false // is hidden (boolean)
  415. }
  416. }
  417. };
  418. chart = service.chart(id, chart);
  419. named.charts[id] = chart;
  420. }
  421. service.begin(chart);
  422. service.set('queries', NumFetch);
  423. service.end();
  424. }
  425. }
  426. }
  427. if(typeof resolver.qtypes !== 'undefined')
  428. service.module.chartFromMembers(service, resolver.qtypes, 'view_resolver_qtypes_' + x, 'Bind, ' + x + ' View, Requests by Query Type', 'requests/s', 'view_' + x, 'named.resolver_qtypes', netdata.chartTypes.stacked, named.base_priority + 14, netdata.chartAlgorithms.incremental, 1, 1);
  429. //if(typeof resolver.cache !== 'undefined')
  430. // service.module.chartFromMembers(service, resolver.cache, 'view_resolver_cache_' + x, 'Bind, ' + x + ' View, Cache Entries', 'entries', 'view_' + x, 'named.resolver_cache', netdata.chartTypes.stacked, named.base_priority + 15, netdata.chartAlgorithms.absolute, 1, 1);
  431. if(typeof resolver.cachestats['CacheHits'] !== 'undefined' && resolver.cachestats['CacheHits'] > 0) {
  432. id = 'named_' + service.name + '.view_resolver_cachehits_' + x;
  433. chart = named.charts[id];
  434. if(typeof chart === 'undefined') {
  435. chart = {
  436. id: id, // the unique id of the chart
  437. name: '', // the unique name of the chart
  438. title: service.name + ' Bind, ' + x + ' View, Resolver Cache Hits', // the title of the chart
  439. units: 'operations/s', // the units of the chart dimensions
  440. family: 'view_' + x, // the family of the chart
  441. context: 'named.resolver_cache_hits', // the context of the chart
  442. type: netdata.chartTypes.area, // the type of the chart
  443. priority: named.base_priority + 1100, // the priority relative to others in the same family
  444. update_every: service.update_every, // the expected update frequency of the chart
  445. dimensions: {
  446. 'CacheHits': {
  447. id: 'CacheHits', // the unique id of the dimension
  448. name: 'hits', // the name of the dimension
  449. algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm
  450. multiplier: 1, // the multiplier
  451. divisor: 1, // the divisor
  452. hidden: false // is hidden (boolean)
  453. },
  454. 'CacheMisses': {
  455. id: 'CacheMisses', // the unique id of the dimension
  456. name: 'misses', // the name of the dimension
  457. algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm
  458. multiplier: -1, // the multiplier
  459. divisor: 1, // the divisor
  460. hidden: false // is hidden (boolean)
  461. }
  462. }
  463. };
  464. chart = service.chart(id, chart);
  465. named.charts[id] = chart;
  466. }
  467. service.begin(chart);
  468. service.set('CacheHits', resolver.cachestats['CacheHits']);
  469. service.set('CacheMisses', resolver.cachestats['CacheMisses']);
  470. service.end();
  471. }
  472. // this is wrong, it contains many types of info:
  473. // 1. CacheHits, CacheMisses - incremental (added above)
  474. // 2. QueryHits, QueryMisses - incremental
  475. // 3. DeleteLRU, DeleteTTL - incremental
  476. // 4. CacheNodes, CacheBuckets - absolute
  477. // 5. TreeMemTotal, TreeMemInUse - absolute
  478. // 6. HeapMemMax, HeapMemTotal, HeapMemInUse - absolute
  479. //if(typeof resolver.cachestats !== 'undefined')
  480. // service.module.chartFromMembers(service, resolver.cachestats, 'view_resolver_cachestats_' + x, 'Bind, ' + x + ' View, Cache Statistics', 'requests/s', 'view_' + x, 'named.resolver_cache_stats', netdata.chartTypes.line, named.base_priority + 1001, netdata.chartAlgorithms.incremental, 1, 1);
  481. //if(typeof resolver.adb !== 'undefined')
  482. // service.module.chartFromMembers(service, resolver.adb, 'view_resolver_adb_' + x, 'Bind, ' + x + ' View, ADB Statistics', 'entries', 'view_' + x, 'named.resolver_adb', netdata.chartTypes.line, named.base_priority + 1002, netdata.chartAlgorithms.absolute, 1, 1);
  483. }
  484. }
  485. }
  486. },
  487. // module.serviceExecute()
  488. // this function is called only from this module
  489. // its purpose is to prepare the request and call
  490. // netdata.serviceExecute()
  491. serviceExecute: function(name, a_url, update_every) {
  492. if(netdata.options.DEBUG === true) netdata.debug(this.name + ': ' + name + ': url: ' + a_url + ', update_every: ' + update_every);
  493. var service = netdata.service({
  494. name: name,
  495. request: netdata.requestFromURL(a_url),
  496. update_every: update_every,
  497. module: this
  498. });
  499. service.execute(this.processResponse);
  500. },
  501. configure: function(config) {
  502. var added = 0;
  503. if(this.enable_autodetect === true) {
  504. this.serviceExecute('local', 'http://localhost:8888/json/v1/server', this.update_every);
  505. added++;
  506. }
  507. if(typeof(config.servers) !== 'undefined') {
  508. var len = config.servers.length;
  509. while(len--) {
  510. if(typeof config.servers[len].update_every === 'undefined')
  511. config.servers[len].update_every = this.update_every;
  512. this.serviceExecute(config.servers[len].name, config.servers[len].url, config.servers[len].update_every);
  513. added++;
  514. }
  515. }
  516. return added;
  517. },
  518. // module.update()
  519. // this is called repeatidly to collect data, by calling
  520. // netdata.serviceExecute()
  521. update: function(service, callback) {
  522. service.execute(function(serv, data) {
  523. service.module.processResponse(serv, data);
  524. callback();
  525. });
  526. }
  527. };
  528. module.exports = named;