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