Add stats reporting to HAProxy namespace driver

fixes bug 1135327

Change-Id: Ib5bc1b66ba57a8d93a5ee181c7cde64893c6b651
This commit is contained in:
Oleg Bondarev 2013-02-28 17:31:03 +04:00
parent c5072518ca
commit b760c07d6a
4 changed files with 106 additions and 1 deletions

View File

@ -31,3 +31,13 @@ HEALTH_MONITOR_HTTPS = 'HTTPS'
SESSION_PERSISTENCE_SOURCE_IP = 'SOURCE_IP'
SESSION_PERSISTENCE_HTTP_COOKIE = 'HTTP_COOKIE'
SESSION_PERSISTENCE_APP_COOKIE = 'APP_COOKIE'
STATS_CURRENT_CONNECTIONS = 'CURRENT_CONNECTIONS'
STATS_MAX_CONNECTIONS = 'MAX_CONNECTIONS'
STATS_CURRENT_SESSIONS = 'CURRENT_SESSIONS'
STATS_MAX_SESSIONS = 'MAX_SESSIONS'
STATS_TOTAL_SESSIONS = 'TOTAL_SESSIONS'
STATS_IN_BYTES = 'IN_BYTES'
STATS_OUT_BYTES = 'OUT_BYTES'
STATS_CONNECTION_ERRORS = 'CONNECTION_ERRORS'
STATS_RESPONSE_ERRORS = 'RESPONSE_ERRORS'

View File

@ -35,6 +35,18 @@ BALANCE_MAP = {
constants.LB_METHOD_SOURCE_IP: 'source'
}
STATS_MAP = {
constants.STATS_CURRENT_CONNECTIONS: 'qcur',
constants.STATS_MAX_CONNECTIONS: 'qmax',
constants.STATS_CURRENT_SESSIONS: 'scur',
constants.STATS_MAX_SESSIONS: 'smax',
constants.STATS_TOTAL_SESSIONS: 'stot',
constants.STATS_IN_BYTES: 'bin',
constants.STATS_OUT_BYTES: 'bout',
constants.STATS_CONNECTION_ERRORS: 'econ',
constants.STATS_RESPONSE_ERRORS: 'eresp'
}
ACTIVE = qconstants.ACTIVE

View File

@ -106,7 +106,40 @@ class HaproxyNSDriver(object):
return False
def get_stats(self, pool_id):
pass
socket_path = self._get_state_file_path(pool_id, 'sock')
if os.path.exists(socket_path):
try:
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(socket_path)
s.send('show stat -1 2 -1\n')
raw_stats = ''
chunk_size = 1024
while True:
chunk = s.recv(chunk_size)
raw_stats += chunk
if len(chunk) < chunk_size:
break
return self._parse_stats(raw_stats)
except socket.error, e:
LOG.warn(_('Error while connecting to stats socket: %s') % e)
return {}
else:
LOG.warn(_('Stats socket not found for pool %s') % pool_id)
return {}
def _parse_stats(self, raw_stats):
stat_lines = raw_stats.splitlines()
if len(stat_lines) < 2:
return {}
stat_names = [line.strip('# ') for line in stat_lines[0].split(',')]
stat_values = [line.strip() for line in stat_lines[1].split(',')]
stats = dict(zip(stat_names, stat_values))
unified_stats = {}
for stat in hacfg.STATS_MAP:
unified_stats[stat] = stats.get(hacfg.STATS_MAP[stat], '')
return unified_stats
def remove_orphans(self, known_pool_ids):
raise NotImplementedError()

View File

@ -129,3 +129,53 @@ class TestHaproxyNSDriver(testtools.TestCase):
])
self.assertTrue(self.driver.exists('pool_id'))
def test_get_stats(self):
raw_stats = ('# pxname,svname,qcur,qmax,scur,smax,slim,stot,bin,bout,'
'dreq,dresp,ereq,econ,eresp,wretr,wredis,status,weight,'
'act,bck,chkfail,chkdown,lastchg,downtime,qlimit,pid,iid,'
'sid,throttle,lbtot,tracked,type,rate,rate_lim,rate_max,'
'check_status,check_code,check_duration,hrsp_1xx,'
'hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,'
'req_rate,req_rate_max,req_tot,cli_abrt,srv_abrt,\n'
'8e271901-69ed-403e-a59b-f53cf77ef208,BACKEND,1,2,3,4,0,'
'10,7764,2365,0,0,,0,0,0,0,UP,1,1,0,,0,103780,0,,1,2,0,,0'
',,1,0,,0,,,,0,0,0,0,0,0,,,,,0,0,\n\n')
raw_stats_empty = ('# pxname,svname,qcur,qmax,scur,smax,slim,stot,bin,'
'bout,dreq,dresp,ereq,econ,eresp,wretr,wredis,'
'status,weight,act,bck,chkfail,chkdown,lastchg,'
'downtime,qlimit,pid,iid,sid,throttle,lbtot,'
'tracked,type,rate,rate_lim,rate_max,check_status,'
'check_code,check_duration,hrsp_1xx,hrsp_2xx,'
'hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,'
'req_rate,req_rate_max,req_tot,cli_abrt,srv_abrt,'
'\n')
with contextlib.nested(
mock.patch.object(self.driver, '_get_state_file_path'),
mock.patch('socket.socket'),
mock.patch('os.path.exists'),
) as (gsp, socket, path_exists):
gsp.side_effect = lambda x, y: '/pool/' + y
path_exists.return_value = True
socket.return_value = socket
socket.recv.return_value = raw_stats
exp_stats = {'CONNECTION_ERRORS': '0',
'CURRENT_CONNECTIONS': '1',
'CURRENT_SESSIONS': '3',
'IN_BYTES': '7764',
'MAX_CONNECTIONS': '2',
'MAX_SESSIONS': '4',
'OUT_BYTES': '2365',
'RESPONSE_ERRORS': '0',
'TOTAL_SESSIONS': '10'}
stats = self.driver.get_stats('pool_id')
self.assertEqual(exp_stats, stats)
socket.recv.return_value = raw_stats_empty
self.assertEqual({}, self.driver.get_stats('pool_id'))
path_exists.return_value = False
socket.reset_mock()
self.assertEqual({}, self.driver.get_stats('pool_id'))
self.assertFalse(socket.called)