Merge "Add plugin Calico Bird"
diff --git a/collectd/files/plugin/collectd_calico_bird.py b/collectd/files/plugin/collectd_calico_bird.py
new file mode 100644
index 0000000..5b519b8
--- /dev/null
+++ b/collectd/files/plugin/collectd_calico_bird.py
@@ -0,0 +1,153 @@
+#!/usr/bin/python
+# Copyright 2017 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if __name__ == '__main__':
+ import collectd_fake as collectd
+else:
+ import collectd
+import re
+import time
+
+import collectd_base as base
+
+NAME = 'calico_bird'
+# Default sampling interval
+INTERVAL = 60
+BIRDCL_BINARY = '/usr/bin/birdcl'
+
+re_memory = re.compile(r"(?P<attribute>.*):\s+"
+ r"(?P<quantity>\d+)\s"
+ r"(?P<multiplier>[kMG ])B\s*")
+
+re_protocol = re.compile(r"(?P<name>\S+)\s+"
+ r"(?P<protocol>\S+)\s+"
+ r"(?P<table>\S+)\s+"
+ r"(?P<state>\S+)\s+"
+ r"(?P<since>\d{2}:\d{2}:\d{2})"
+ r"(?P<info>.*)")
+
+
+def protocol_metric(line):
+ re_match = re_protocol.match(line)
+ if re_match:
+ if re_match.group("protocol") == "BGP":
+ return gen_metric(
+ 'bgp_up', re_match.group('state') == 'up',
+ {
+ 'bird_protocol_instance': re_match.group('name'),
+ }
+ )
+
+
+def memory_metric(line):
+ re_match = re_memory.match(line)
+ if re_match:
+ quantity = int(re_match.group("quantity"))
+ for m in " kMG":
+ if re_match.group("multiplier") == m:
+ break
+ quantity *= 1024
+ mname = 'memory_' + re_match.group(
+ 'attribute').lower().replace(' ', '_')
+ return gen_metric(
+ mname, quantity
+ )
+
+
+def gen_metric(name, val, meta={}):
+ ret_metric = {
+ 'type_instance': name,
+ 'values': val,
+ }
+ if meta:
+ ret_metric['meta'] = meta
+ return ret_metric
+
+
+class CalicoBirdPlugin(base.Base):
+
+ _checks = {
+ 'memory': {
+ 'cmd_args': ['show', 'memory'],
+ 'func': memory_metric,
+ },
+ 'protocol': {
+ 'cmd_args': ['show', 'protocols'],
+ 'func': protocol_metric,
+ },
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(CalicoBirdPlugin, self).__init__(*args, **kwargs)
+ self.plugin = NAME
+ self._socketfiles = {}
+
+ def config_callback(self, config):
+ super(CalicoBirdPlugin, self).config_callback(config)
+ for node in config.children:
+ m = re.search('^ipv(?P<ip_version>[46])_socket$', node.key.lower())
+ if m:
+ self._socketfiles[m.group('ip_version')] = node.values[0]
+
+ def _run_birdcl_command(self, sockf, args):
+ cmd = [
+ BIRDCL_BINARY,
+ '-s',
+ sockf
+ ] + args
+ retcode, out, err = self.execute(cmd, shell=False)
+ if retcode == 0:
+ return out
+ msg = "Failed to execute {} '{}'".format(cmd, err)
+ raise base.CheckException(msg)
+
+ def itermetrics(self):
+ for ipv, sockf in self._socketfiles.items():
+ for lcname, lcheck in self._checks.items():
+ out = self._run_birdcl_command(sockf, lcheck['cmd_args'])
+ for metric in filter(
+ None,
+ [lcheck['func'](line) for line in out.split('\n')]
+ ):
+ if not metric.get('meta', False):
+ metric['meta'] = {}
+ metric['meta'].update({'ip_version': ipv})
+ yield metric
+
+
+plugin = CalicoBirdPlugin(collectd)
+
+
+def init_callback():
+ plugin.restore_sigchld()
+
+
+def config_callback(conf):
+ plugin.config_callback(conf)
+
+
+def read_callback():
+ plugin.read_callback()
+
+if __name__ == '__main__':
+ collectd.load_configuration(plugin)
+ plugin.read_callback()
+ collectd.info('Sleeping for {}s'.format(INTERVAL))
+ time.sleep(INTERVAL)
+ plugin.read_callback()
+else:
+ collectd.register_init(init_callback)
+ collectd.register_config(config_callback)
+ collectd.register_read(read_callback, INTERVAL)