Skip to content

Commit f946865

Browse files
author
Terry Hardie
committed
Bind collector: Adding support for /server section of bind9
for xml_v3 and json_v1
1 parent f4a523f commit f946865

File tree

5 files changed

+5546
-0
lines changed

5 files changed

+5546
-0
lines changed

docs/collectors/BindCollector.md

100644100755
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ publish | resolver, server, zonemgmt, sockets, memory, | Available stats:<br>
3232
| list
3333
publish_view_bind | False | | bool
3434
publish_view_meta | False | | bool
35+
data_format | xml_v2 | Bind stats version:<br>
36+
- xml_v2 (Original bind stats version from 9.5)<br>
37+
- xml_v3 (New xml version)<br>
38+
- json_v1 (JSON replacement for XML)<br>
39+
| str
3540

3641
#### Example Output
3742

src/collectors/bind/bind.py

100644100755
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ def get_default_config_help(self):
3535
" - memory (Global memory usage)\n",
3636
'publish_view_bind': "",
3737
'publish_view_meta': "",
38+
'data_format': "Bind stats version:\n" +
39+
" - xml_v2 (Original bind stats version from 9.5)\n" +
40+
" - xml_v3 (New xml version)\n" +
41+
" - json_v1 (JSON replacement for XML)\n",
3842
})
3943
return config_help
4044

@@ -63,6 +67,7 @@ def get_default_config(self):
6367
# By default we don't publish these special views
6468
'publish_view_bind': False,
6569
'publish_view_meta': False,
70+
'data_format': 'xml_v2',
6671
})
6772
return config
6873

@@ -73,7 +78,62 @@ def clean_counter(self, name, value):
7378
self.publish(name, value)
7479

7580
def collect(self):
81+
if self.config['data_format'] == 'json_v1':
82+
return self.collect_json_v1()
83+
if self.config['data_format'] == 'xml_v3':
84+
return self.collect_xml_v3()
85+
if self.config['data_format'] == 'xml_v2':
86+
return self.collect_xml_v2()
87+
88+
def collect_json_v1(self):
89+
try:
90+
# Try newest interface first (JSON has least impact)
91+
import json
92+
req = urllib2.urlopen('http://%s:%d/json/v1/status' % (
93+
self.config['host'], int(self.config['port'])))
94+
except Exception, e:
95+
self.log.error('JSON v1 not supported: %s', e)
96+
return {}
97+
98+
if 'server' in self.config['publish']:
99+
try:
100+
req = urllib2.urlopen('http://%s:%d/json/v1/server' % (
101+
self.config['host'], int(self.config['port'])))
102+
except Exception, e:
103+
self.log.error('Couldnt connect to bind: %s', e)
104+
return {}
105+
106+
response = json.load(req)
107+
self.parse_json_v1_server(response)
108+
109+
def collect_xml_v3(self):
110+
try:
111+
# Try newer interface first
112+
req = urllib2.urlopen('http://%s:%d/xml/v3/status' % (
113+
self.config['host'], int(self.config['port'])))
114+
except Exception, e:
115+
self.log.error('XML v3 not supported: %s', e)
116+
return {}
117+
118+
if 'server' in self.config['publish']:
119+
try:
120+
req = urllib2.urlopen('http://%s:%d/xml/v3/server' % (
121+
self.config['host'], int(self.config['port'])))
122+
except Exception, e:
123+
self.log.error('Couldnt connect to bind: %s', e)
124+
return {}
125+
# Proceed with v3 parsing
126+
tree = ElementTree.parse(req)
127+
128+
if not tree:
129+
raise ValueError("Corrupt XML file, no statistics found")
130+
131+
self.parse_xml_v3_server(tree)
132+
133+
def collect_xml_v2(self):
76134
try:
135+
# NOTE: Querying this node on a large server can impact bind
136+
# answering queriesfor sometimes hundreds of milliseconds.
77137
req = urllib2.urlopen('http://%s:%d/' % (
78138
self.config['host'], int(self.config['port'])))
79139
except Exception, e:
@@ -87,6 +147,10 @@ def collect(self):
87147

88148
root = tree.find('bind/statistics')
89149

150+
if not root:
151+
raise ValueError(
152+
"Missing bind/statistics tree - Wrong data_format?")
153+
90154
if 'resolver' in self.config['publish']:
91155
for view in root.findall('views/view'):
92156
name = view.find('name').text
@@ -153,3 +217,45 @@ def collect(self):
153217
'memory.%s' % counter.tag,
154218
int(counter.text)
155219
)
220+
221+
def parse_xml_v3_server(self, root):
222+
for counters in root.findall('server/counters'):
223+
for counter in counters:
224+
self.clean_counter(
225+
'server.counters.%s.%s' % (counters.attrib['type'],
226+
counter.attrib['name']),
227+
int(counter.text)
228+
)
229+
230+
for view in root.findall('views/view'):
231+
for counters in view.iter('counters'):
232+
for counter in counters:
233+
self.clean_counter(
234+
'views.%s.counters.%s.%s' % (view.attrib['name'],
235+
counters.attrib['type'],
236+
counter.attrib['name']),
237+
int(counter.text)
238+
)
239+
240+
def parse_json_v1_server(self, response):
241+
for counter_metric_name in [
242+
'nsstat', 'opcode', 'qtype', 'rcode', 'zonestat'
243+
]:
244+
for counter in response[counter_metric_name+'s']:
245+
self.clean_counter(
246+
'server.counters.%s.%s' % (counter_metric_name, counter),
247+
int(response[counter_metric_name+'s'][counter])
248+
)
249+
250+
for view in response['views']:
251+
for counters in response['views'][view]:
252+
# This mapping maps from XML v3 layout to JSON v1
253+
for section_name in response['views'][view]['resolver']:
254+
for counter in \
255+
response['views'][view]['resolver'][section_name]:
256+
self.clean_counter(
257+
'views.%s.counters.%s.%s' %
258+
(view, section_name, counter),
259+
int(response['views'][view]['resolver']
260+
[section_name][counter])
261+
)

0 commit comments

Comments
 (0)