[WORKER] Implement new STATS request

STATS requests will now return usage information for each LB
defined on a device (TCP, HTTP, or both). This change introduces
a new StatisticsManager class for managing temporary storage of
HAProxy statistics to deal with the fact that its reported values
are reset on restart. Also add tests for the new class.

Removed the LBStatistics class since it wasn't necessary.

Change-Id: I56177a829650b2206ee855fdf4756ed52825e936
This commit is contained in:
David Shrewsbury
2013-11-11 10:10:26 -05:00
parent b99bc9f201
commit bbd4eb0ad0
12 changed files with 490 additions and 156 deletions

View File

@@ -21,7 +21,7 @@ from libra import __release__ as libra_release
from libra.common.exc import DeletedStateError
from libra.common.faults import BadRequest
from libra.openstack.common import log
from libra.worker.drivers.base import LoadBalancerDriver
from libra.worker.drivers import base
LOG = log.getLogger(__name__)
@@ -71,8 +71,7 @@ class LBaaSController(object):
elif action == 'ARCHIVE':
return self._action_archive()
elif action == 'STATS':
# TODO: Implement new STATS function
return self._action_ping()
return self._action_stats()
elif action == 'PING':
return self._action_ping()
elif action == 'DIAGNOSTICS':
@@ -203,15 +202,15 @@ class LBaaSController(object):
if 'algorithm' in current_lb:
algo = current_lb['algorithm'].upper()
if algo == 'ROUND_ROBIN':
algo = LoadBalancerDriver.ROUNDROBIN
algo = base.LoadBalancerDriver.ROUNDROBIN
elif algo == 'LEAST_CONNECTIONS':
algo = LoadBalancerDriver.LEASTCONN
algo = base.LoadBalancerDriver.LEASTCONN
else:
LOG.error("Invalid algorithm: %s" % algo)
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_FAILURE
return self.msg
else:
algo = LoadBalancerDriver.ROUNDROBIN
algo = base.LoadBalancerDriver.ROUNDROBIN
try:
self.driver.set_algorithm(current_lb['protocol'], algo)
@@ -451,26 +450,69 @@ class LBaaSController(object):
"""
try:
stats = self.driver.get_status()
nodes = self.driver.get_status()
except NotImplementedError:
error = "Selected driver does not support PING action."
LOG.error(error)
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_FAILURE
self.msg[self.ERROR_FIELD] = error
except DeletedStateError:
LOG.info("Invalid operation PING on a deleted LB")
error = "Invalid operation PING on a deleted LB."
LOG.error(error)
self.msg['status'] = 'DELETED'
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_FAILURE
self.msg[self.ERROR_FIELD] = error
except Exception as e:
LOG.error("PING failed: %s, %s" % (e.__class__, e))
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_FAILURE
self.msg[self.ERROR_FIELD] = str(e)
else:
node_status = stats.node_status_map()
self.msg['nodes'] = []
for node in node_status.keys():
self.msg['nodes'].append({'id': node,
'status': node_status[node]})
for node, status in nodes:
self.msg['nodes'].append({'id': node, 'status': status})
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_SUCCESS
return self.msg
def _action_stats(self):
"""
Get load balancer statistics
This type of request gets the number of bytes out for each load
balancer defined on the device. If both a TCP and HTTP load
balancer exist, we report on each in a single response.
"""
try:
start, end, statistics = self.driver.get_statistics()
except NotImplementedError:
error = "Selected driver does not support STATS action."
LOG.error(error)
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_FAILURE
self.msg[self.ERROR_FIELD] = error
return self.msg
except DeletedStateError:
error = "Invalid operation STATS on a deleted LB."
LOG.error(error)
self.msg['status'] = 'DELETED'
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_FAILURE
self.msg[self.ERROR_FIELD] = error
return self.msg
except Exception as e:
LOG.error("STATS failed: %s, %s" % (e.__class__, e))
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_FAILURE
self.msg[self.ERROR_FIELD] = str(e)
return self.msg
self.msg['utc_start'] = start
self.msg['utc_end'] = end
self.msg['loadBalancers'] = []
# We should have a list of tuples pairing the number of bytes
# out with the protocol/LB.
for proto, bytes_out in statistics:
self.msg['loadBalancers'].append({'protocol': proto,
'bytes_out': bytes_out})
self.msg[self.RESPONSE_FIELD] = self.RESPONSE_SUCCESS
return self.msg