156 lines
6.5 KiB
Python
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"
|