blob: f571796e71961af5b559b8619057a09004b61c70 [file] [log] [blame]
#!/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 requests
import collectd_base as base
NAME = 'calico_felix'
# Default sampling interval
INTERVAL = 60
class CalicoFelixPlugin(base.Base):
def __init__(self, *args, **kwargs):
super(CalicoFelixPlugin, self).__init__(*args, **kwargs)
self.plugin = NAME
self.session = requests.Session()
self.session.mount(
'http://',
requests.adapters.HTTPAdapter(max_retries=self.max_retries)
)
self.session.mount(
'https://',
requests.adapters.HTTPAdapter(max_retries=self.max_retries)
)
self.url = None
def config_callback(self, config):
super(CalicoFelixPlugin, self).config_callback(config)
for node in config.children:
self.logger.debug("Got config request for '{}': {}".format(
node.key.lower(), node.values[0])
)
if node.key.lower() == "url":
self.url = node.values[0]
def itermetrics(self):
if self.url:
self.logger.debug("Requesting URL {}".format(
self.url)
)
try:
r = self.session.get(self.url, timeout=self.timeout)
except Exception as e:
msg = "Got exception for '{}': {}".format(self.url, e)
raise base.CheckException(msg)
if r.status_code != 200:
self.logger.error(
("{} responded with code {} "
"").format(self.url,
r.status_code))
raise base.CheckException(
"Failed to gather Calico Felix metrics ({})".format(
r.status_code
)
)
self.logger.debug(
"Got response from {}: '{}'"
"".format(self.url, r.text))
# Example payload:
# # HELP felix_active_local_endpoints Number
# # of active endpoints on this host.
# # TYPE felix_active_local_endpoints gauge
# felix_active_local_endpoints 1
# # HELP felix_iptables_chains Number of active iptables chains.
# # TYPE felix_iptables_chains gauge
# felix_iptables_chains{ip_version="4",table="filter"} 14
# felix_iptables_chains{ip_version="4",table="nat"} 6
# felix_iptables_chains{ip_version="4",table="raw"} 6
# felix_iptables_chains{ip_version="6",table="filter"} 14
# felix_iptables_chains{ip_version="6",table="nat"} 6
# felix_iptables_chains{ip_version="6",table="raw"} 6
# # HELP go_goroutines Number of goroutines that currently exist.
# # TYPE go_goroutineqs gauge
# go_goroutines 39
for l in r.text.split('\n'):
# Line is empty or is a comment
if not l or l.startswith('#'):
continue
(name, rval) = l.split()
self.logger.debug(
"Got val for {}: '{}'".format(name, rval))
# For some metrics, remove the existing felix prefix
# to ensure homogeneity
if name.startswith('felix_'):
name = name.replace('felix_', '', 1)
# Initialization of returned metric
ret_metric = {
'values': rval
}
# Metric can have implicit dimensions. For example:
# felix_iptables_rules{ip_version="4",table="filter"}
m = re.search(
'^(?P<name>[^{]+)(?:{(?P<dimensions>.[^}]+)})?$',
name)
if not m:
self.logger.error(
"Error parsing metric name {}".format(
name))
continue
if m.group('dimensions'):
name = m.group('name')
meta = {}
for d in m.group('dimensions').split(','):
(k, v) = d.split('=')
meta[k] = v.strip('"')
if len(meta) > 0:
ret_metric['meta'] = meta
ret_metric['type_instance'] = m.group('name')
yield ret_metric
plugin = CalicoFelixPlugin(collectd)
def config_callback(conf):
plugin.config_callback(conf)
def notification_callback(notification):
plugin.notification_callback(notification)
def read_callback():
plugin.conditional_read_callback()
if __name__ == '__main__':
collectd.load_configuration(plugin)
plugin.read_callback()
else:
collectd.register_config(config_callback)
collectd.register_notification(notification_callback)
collectd.register_read(read_callback, INTERVAL)