Merge "Add stats reporting to HAProxy namespace driver"
This commit is contained in:
commit
cc33ba8e0c
@ -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'
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user