]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add stats reporting to HAProxy namespace driver
authorOleg Bondarev <obondarev@mirantis.com>
Thu, 28 Feb 2013 13:31:03 +0000 (17:31 +0400)
committerOleg Bondarev <obondarev@mirantis.com>
Thu, 28 Feb 2013 15:10:22 +0000 (19:10 +0400)
fixes bug 1135327

Change-Id: Ib5bc1b66ba57a8d93a5ee181c7cde64893c6b651

quantum/plugins/services/agent_loadbalancer/constants.py
quantum/plugins/services/agent_loadbalancer/drivers/haproxy/cfg.py
quantum/plugins/services/agent_loadbalancer/drivers/haproxy/namespace_driver.py
quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_namespace_driver.py

index 82b049f17373475e76bb99e49c7be403e5e4e0b2..afd3aacbf3e94144e1a92aded665950e538ff343 100644 (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'
index 936bcff3031ac9c6abb6bebf95c9773479c01604..d45642cec20667db26aecf36f989db9ee026d7a6 100644 (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
 
 
index f4df283d319e567818f137ba3718b0ed1e0d3427..d262b2322498a42e48cb250e4a63d7fb2239f460 100644 (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()
index 16c7e3593dc9d15072d3fb6cb5592e0441920e2b..56445e39db33580ecc562841d67e7dcfd2f706d6 100644 (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)