Add new STATS worker message.
This implements phase 1 of monitoring by basically checking that HAProxy is alive. A new STATS message is recognized. The response to this message will, so far, contain only a PASS/FAIL response, and an error message in the case of FAIL. The next phase will be to add backend node checking. This will likely be combined with reporting usage statistics at a later phase. Change-Id: Ie381c4cbb91dad90074f5addd3c4e3cf24dd33d8
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class ServiceUnavailable(Exception):
|
||||
pass
|
||||
@@ -64,6 +64,8 @@ class LBaaSController(object):
|
||||
return self._action_discover()
|
||||
elif action == 'ARCHIVE':
|
||||
return self._action_archive()
|
||||
elif action == 'STATS':
|
||||
return self._action_stats()
|
||||
else:
|
||||
self.logger.error("Invalid `%s` value: %s" %
|
||||
(self.ACTION_FIELD, action))
|
||||
@@ -331,3 +333,21 @@ class LBaaSController(object):
|
||||
else:
|
||||
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_SUCCESS
|
||||
return self.msg
|
||||
|
||||
def _action_stats(self):
|
||||
""" Get load balancer statistics. """
|
||||
try:
|
||||
# TODO: Do something with the returned statistics
|
||||
self.driver.get_stats(protocol=None)
|
||||
except NotImplementedError:
|
||||
error = "Selected driver does not support STATS action."
|
||||
self.logger.error(error)
|
||||
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_FAILURE
|
||||
self.msg[self.ERROR_FIELD] = error
|
||||
except Exception as e:
|
||||
self.logger.error("STATS failed: %s, %s" % (e.__class__, e))
|
||||
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_FAILURE
|
||||
self.msg[self.ERROR_FIELD] = str(e)
|
||||
else:
|
||||
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_SUCCESS
|
||||
return self.msg
|
||||
|
||||
50
libra/worker/drivers/haproxy/query.py
Normal file
50
libra/worker/drivers/haproxy/query.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import subprocess
|
||||
|
||||
|
||||
class HAProxyQuery(object):
|
||||
""" Class used for querying the HAProxy statistics socket. """
|
||||
|
||||
def __init__(self, stats_socket):
|
||||
"""
|
||||
stats_socket
|
||||
Path to the HAProxy statistics socket file.
|
||||
"""
|
||||
self.socket = stats_socket
|
||||
|
||||
def _query(self, query):
|
||||
"""
|
||||
Send the given query to the haproxy statistics socket.
|
||||
|
||||
Return the output of a successful query as a string with trailing
|
||||
newlines removed, or raise an Exception if the query fails.
|
||||
"""
|
||||
cmd = 'echo "%s" | sudo -n /usr/bin/socat stdio %s' % \
|
||||
(query, self.socket)
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(cmd, shell=True)
|
||||
except subprocess.CalledProcessError:
|
||||
raise Exception("HAProxy '%s' query failed." % query)
|
||||
|
||||
return output.rstrip()
|
||||
|
||||
def show_info(self):
|
||||
""" Get and parse output from 'show info' command. """
|
||||
results = self._query('show info')
|
||||
list_results = results.split('\n')
|
||||
# TODO: Parse the results into a well defined format.
|
||||
return list_results
|
||||
@@ -12,13 +12,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import csv
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from libra.common.exc import ServiceUnavailable
|
||||
from libra.common.lbstats import LBStatistics
|
||||
from libra.worker.drivers.haproxy.services_base import ServicesBase
|
||||
from libra.worker.drivers.haproxy.query import HAProxyQuery
|
||||
|
||||
|
||||
class UbuntuServices(ServicesBase):
|
||||
@@ -141,32 +140,13 @@ class UbuntuServices(ServicesBase):
|
||||
"""
|
||||
|
||||
if not os.path.exists(self._haproxy_pid):
|
||||
raise ServiceUnavailable()
|
||||
raise Exception("HAProxy is not running.")
|
||||
|
||||
stats = LBStatistics()
|
||||
query = HAProxyQuery('/var/run/haproxy-stats.socket')
|
||||
|
||||
cmd = 'echo "show stat" | ' \
|
||||
'sudo -n /usr/bin/socat stdio /var/run/haproxy-stats.socket'
|
||||
try:
|
||||
csv_output = subprocess.check_output(cmd, shell=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise Exception("Failed to get statistics: %s" % e)
|
||||
|
||||
# Remove leading '# ' from string and trailing newlines
|
||||
csv_output = csv_output[2:].rstrip()
|
||||
# Turn string into a list, removing last two empty lines
|
||||
csv_lines = csv_output.split('\n')
|
||||
|
||||
proxy_name = "%s-in" % protocol.lower()
|
||||
service_name = "FRONTEND"
|
||||
|
||||
reader = csv.DictReader(csv_lines)
|
||||
for row in reader:
|
||||
if row['pxname'] == proxy_name and row['svname'] == service_name:
|
||||
if row['bout']:
|
||||
stats.bytes_out = long(row['bout'])
|
||||
if row['bin']:
|
||||
stats.bytes_in = long(row['bin'])
|
||||
break
|
||||
# TODO: Do something with the returned results. For now, we are
|
||||
# basically just treating this as a 'ping' to the process.
|
||||
query.show_info()
|
||||
|
||||
return stats
|
||||
|
||||
Reference in New Issue
Block a user