From: Oleg Bondarev Date: Thu, 28 Feb 2013 13:31:03 +0000 (+0400) Subject: Add stats reporting to HAProxy namespace driver X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=b760c07d6af9a6dc47e3b45359c280b4a4d7e995;p=openstack-build%2Fneutron-build.git Add stats reporting to HAProxy namespace driver fixes bug 1135327 Change-Id: Ib5bc1b66ba57a8d93a5ee181c7cde64893c6b651 --- diff --git a/quantum/plugins/services/agent_loadbalancer/constants.py b/quantum/plugins/services/agent_loadbalancer/constants.py index 82b049f17..afd3aacbf 100644 --- a/quantum/plugins/services/agent_loadbalancer/constants.py +++ b/quantum/plugins/services/agent_loadbalancer/constants.py @@ -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' diff --git a/quantum/plugins/services/agent_loadbalancer/drivers/haproxy/cfg.py b/quantum/plugins/services/agent_loadbalancer/drivers/haproxy/cfg.py index 936bcff30..d45642cec 100644 --- a/quantum/plugins/services/agent_loadbalancer/drivers/haproxy/cfg.py +++ b/quantum/plugins/services/agent_loadbalancer/drivers/haproxy/cfg.py @@ -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 diff --git a/quantum/plugins/services/agent_loadbalancer/drivers/haproxy/namespace_driver.py b/quantum/plugins/services/agent_loadbalancer/drivers/haproxy/namespace_driver.py index f4df283d3..d262b2322 100644 --- a/quantum/plugins/services/agent_loadbalancer/drivers/haproxy/namespace_driver.py +++ b/quantum/plugins/services/agent_loadbalancer/drivers/haproxy/namespace_driver.py @@ -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() diff --git a/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_namespace_driver.py b/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_namespace_driver.py index 16c7e3593..56445e39d 100644 --- a/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_namespace_driver.py +++ b/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_namespace_driver.py @@ -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)