dns_query_time.chart.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. # -*- coding: utf-8 -*-
  2. # Description: dns_query_time netdata python.d module
  3. # Author: ilyam8
  4. # SPDX-License-Identifier: GPL-3.0-or-later
  5. from random import choice
  6. from socket import getaddrinfo, gaierror
  7. from threading import Thread
  8. try:
  9. import dns.message
  10. import dns.query
  11. import dns.name
  12. DNS_PYTHON = True
  13. except ImportError:
  14. DNS_PYTHON = False
  15. try:
  16. from queue import Queue
  17. except ImportError:
  18. from Queue import Queue
  19. from bases.FrameworkServices.SimpleService import SimpleService
  20. update_every = 5
  21. class Service(SimpleService):
  22. def __init__(self, configuration=None, name=None):
  23. SimpleService.__init__(self, configuration=configuration, name=name)
  24. self.order = list()
  25. self.definitions = dict()
  26. self.timeout = self.configuration.get('response_timeout', 4)
  27. self.aggregate = self.configuration.get('aggregate', True)
  28. self.domains = self.configuration.get('domains')
  29. self.server_list = self.configuration.get('dns_servers')
  30. def check(self):
  31. if not DNS_PYTHON:
  32. self.error("'python-dnspython' package is needed to use dns_query_time.chart.py")
  33. return False
  34. self.timeout = self.timeout if isinstance(self.timeout, int) else 4
  35. if not all([self.domains, self.server_list,
  36. isinstance(self.server_list, str), isinstance(self.domains, str)]):
  37. self.error("server_list and domain_list can't be empty")
  38. return False
  39. else:
  40. self.domains, self.server_list = self.domains.split(), self.server_list.split()
  41. for ns in self.server_list:
  42. if not check_ns(ns):
  43. self.info('Bad NS: %s' % ns)
  44. self.server_list.remove(ns)
  45. if not self.server_list:
  46. return False
  47. data = self._get_data(timeout=1)
  48. down_servers = [s for s in data if data[s] == -100]
  49. for down in down_servers:
  50. down = down[3:].replace('_', '.')
  51. self.info('Removed due to non response %s' % down)
  52. self.server_list.remove(down)
  53. if not self.server_list:
  54. return False
  55. self.order, self.definitions = create_charts(aggregate=self.aggregate, server_list=self.server_list)
  56. return True
  57. def _get_data(self, timeout=None):
  58. return dns_request(self.server_list, timeout or self.timeout, self.domains)
  59. def dns_request(server_list, timeout, domains):
  60. threads = list()
  61. que = Queue()
  62. result = dict()
  63. def dns_req(ns, t, q):
  64. domain = dns.name.from_text(choice(domains))
  65. request = dns.message.make_query(domain, dns.rdatatype.A)
  66. try:
  67. resp = dns.query.udp(request, ns, timeout=t)
  68. if (resp.rcode() == dns.rcode.NOERROR and resp.answer):
  69. query_time = resp.time * 1000
  70. else:
  71. query_time = -100
  72. except dns.exception.Timeout:
  73. query_time = -100
  74. finally:
  75. q.put({'_'.join(['ns', ns.replace('.', '_')]): query_time})
  76. for server in server_list:
  77. th = Thread(target=dns_req, args=(server, timeout, que))
  78. th.start()
  79. threads.append(th)
  80. for th in threads:
  81. th.join()
  82. result.update(que.get())
  83. return result
  84. def check_ns(ns):
  85. try:
  86. return getaddrinfo(ns, 'domain')[0][4][0]
  87. except gaierror:
  88. return False
  89. def create_charts(aggregate, server_list):
  90. if aggregate:
  91. order = ['dns_group']
  92. definitions = {
  93. 'dns_group': {
  94. 'options': [None, 'DNS Response Time', 'ms', 'name servers', 'dns_query_time.response_time', 'line'],
  95. 'lines': []
  96. }
  97. }
  98. for ns in server_list:
  99. dim = [
  100. '_'.join(['ns', ns.replace('.', '_')]),
  101. ns,
  102. 'absolute',
  103. ]
  104. definitions['dns_group']['lines'].append(dim)
  105. return order, definitions
  106. else:
  107. order = [''.join(['dns_', ns.replace('.', '_')]) for ns in server_list]
  108. definitions = dict()
  109. for ns in server_list:
  110. definitions[''.join(['dns_', ns.replace('.', '_')])] = {
  111. 'options': [None, 'DNS Response Time', 'ms', ns, 'dns_query_time.response_time', 'area'],
  112. 'lines': [
  113. [
  114. '_'.join(['ns', ns.replace('.', '_')]),
  115. ns,
  116. 'absolute',
  117. ]
  118. ]
  119. }
  120. return order, definitions