monasca-agent/monasca_agent/collector/checks_d/lighttpd.py

156 lines
6.5 KiB
Python

# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
# 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.
from six.moves import urllib
from monasca_agent.collector.checks import AgentCheck
from monasca_agent.collector.checks.utils import add_basic_auth
from monasca_agent.common.util import headers
class Lighttpd(AgentCheck):
"""Tracks basic connection/requests/workers metrics
See http://redmine.lighttpd.net/projects/1/wiki/Docs_ModStatus for Lighttpd details
See http://redmine.lighttpd.net/projects/lighttpd2/wiki/Mod_status for Lighttpd2 details
"""
URL_SUFFIX_PER_VERSION = {
1: '?auto',
2: '?format=plain',
'Unknown': '?auto'
}
GAUGES = {
'IdleServers': 'lighttpd.performance.idle_server',
'BusyServers': 'lighttpd.performance.busy_servers',
'Uptime': 'lighttpd.performance.uptime',
'Total kBytes': 'lighttpd.net.bytes',
'Total Accesses': 'lighttpd.net.hits',
'memory_usage': 'lighttpd.performance.memory_usage',
'requests_avg': 'lighttpd.net.requests_avg',
'traffic_out_avg': 'lighttpd.net.bytes_out_avg',
'traffic_in_avg': 'lighttpd.net.bytes_in_avg',
'connections_avg': 'lighttpd.net.connections_avg',
'connection_state_start': 'lighttpd.connections.state_start',
'connection_state_read_header': 'lighttpd.connections.state_read_header',
'connection_state_handle_request': 'lighttpd.connections.state_handle_request',
'connection_state_write_response': 'lighttpd.connections.state_write_response',
'connection_state_keep_alive': 'lighttpd.connections.state_keep_alive',
'requests_avg_5sec': 'lighttpd.net.requests_avg_5sec',
'traffic_out_avg_5sec': 'lighttpd.net.bytes_out_avg_5sec',
'traffic_in_avg_5sec': 'lighttpd.net.bytes_in_avg_5sec',
'connections_avg_5sec': 'lighttpd.net.connections_avg_5sec',
}
COUNTERS = {
'requests_abs': 'lighttpd.net.requests_total',
'traffic_out_abs': 'lighttpd.net.bytes_out',
'traffic_in_abs': 'lighttpd.net.bytes_in',
'connections_abs': 'lighttpd.net.connections_total',
'status_1xx': 'lighttpd.response.status_1xx',
'status_2xx': 'lighttpd.response.status_2xx',
'status_3xx': 'lighttpd.response.status_3xx',
'status_4xx': 'lighttpd.response.status_4xx',
'status_5xx': 'lighttpd.response.status_5xx',
}
RATES = {
'Total kBytes': 'lighttpd.net.bytes_per_s',
'Total Accesses': 'lighttpd.net.request_per_s'
}
def __init__(self, name, init_config, agent_config, instances=None):
AgentCheck.__init__(self, name, init_config, agent_config, instances)
self.assumed_url = {}
def check(self, instance):
if 'lighttpd_status_url' not in instance:
raise Exception("Missing 'lighttpd_status_url' variable in Lighttpd config")
url = self.assumed_url.get(instance['lighttpd_status_url'], instance['lighttpd_status_url'])
dimensions = self._set_dimensions(None, instance)
self.log.debug("Connecting to %s" % url)
req = urllib.request.Request(url, None, headers(self.agent_config))
if 'user' in instance and 'password' in instance:
add_basic_auth(req, instance['user'], instance['password'])
request = urllib.request.urlopen(req)
headers_resp = request.info().headers
server_version = self._get_server_version(headers_resp)
response = request.read()
metric_count = 0
# Loop through and extract the numerical values
for line in response.split('\n'):
values = line.split(': ')
if len(values) == 2: # match
metric, value = values
try:
value = float(value)
except ValueError:
continue
# Special case: kBytes => bytes
if metric == 'Total kBytes':
value *= 1024
# Send metric as a gauge, if applicable
if metric in self.GAUGES:
metric_count += 1
metric_name = self.GAUGES[metric]
self.gauge(metric_name, value, dimensions=dimensions)
# Send metric as a rate, if applicable
if metric in self.RATES:
metric_count += 1
metric_name = self.RATES[metric]
self.rate(metric_name, value, dimensions=dimensions)
# Send metric as a counter, if applicable
if metric in self.COUNTERS:
metric_count += 1
metric_name = self.COUNTERS[metric]
self.increment(metric_name, value, dimensions=dimensions)
if metric_count == 0:
url_suffix = self.URL_SUFFIX_PER_VERSION[server_version]
if self.assumed_url.get(instance['lighttpd_status_url'],
None) is None and url[-len(url_suffix):] != url_suffix:
self.assumed_url[instance['lighttpd_status_url']] = '%s%s' % (url, url_suffix)
self.log.warn(
"Assuming url was not correct. Trying to add %s suffix to the url" %
url_suffix)
self.check(instance)
else:
raise Exception(
"No metrics were fetched for this instance. "
"Make sure that %s is the proper url." %
instance['lighttpd_status_url'])
def _get_server_version(self, headers):
for h in headers:
if "Server:" not in h:
continue
try:
version = int(h.split('/')[1][0])
except Exception as e:
self.log.debug("Error while trying to get server version %s" % str(e))
version = "Unknown"
self.log.debug("Lighttpd server version is %s" % version)
return version
self.log.debug("Lighttpd server version is Unknown")
return "Unknown"