RIFT OSM R1 Initial Submission
[osm/SO.git] / rwlaunchpad / plugins / rwmonparam / test / utest_mon_params.py
diff --git a/rwlaunchpad/plugins/rwmonparam/test/utest_mon_params.py b/rwlaunchpad/plugins/rwmonparam/test/utest_mon_params.py
new file mode 100755 (executable)
index 0000000..a0817d7
--- /dev/null
@@ -0,0 +1,933 @@
+#!/usr/bin/env python3
+
+# 
+#   Copyright 2016 RIFT.IO Inc
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+
+
+import asyncio
+import base64
+import logging
+import os
+import sys
+import tornado.escape
+import tornado.platform.asyncio
+import tornado.testing
+import tornado.web
+import unittest
+import xmlrunner
+
+import rift.tasklets.rwmonparam.vnfr_core as mon_params
+
+
+from gi.repository import VnfrYang
+
+logger = logging.getLogger("mon_params_test.py")
+
+
+class AsyncioTornadoTest(tornado.testing.AsyncHTTPTestCase):
+    def setUp(self):
+        self._loop = asyncio.get_event_loop()
+        super().setUp()
+
+    def get_new_ioloop(self):
+        return tornado.platform.asyncio.AsyncIOMainLoop()
+
+
+class MonParamsPingStatsTest(AsyncioTornadoTest):
+    ping_path = r"/api/v1/ping/stats"
+    ping_response = {
+            'ping-request-tx-count': 5,
+            'ping-response-rx-count': 10
+            }
+
+    mon_param_msg = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam()
+    mon_param_msg.from_dict({
+            'id': '1',
+            'name': 'ping-request-tx-count',
+            'json_query_method': "NAMEKEY",
+            'http_endpoint_ref': ping_path,
+            'value_type': "INT",
+            'description': 'no of ping requests',
+            'group_tag': 'Group1',
+            'widget_type': 'COUNTER',
+            'units': 'packets'
+            })
+
+    endpoint_msg = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_HttpEndpoint()
+    endpoint_msg.from_dict({
+        'path': ping_path,
+        'polling_interval_secs': 1,
+        'username': 'admin',
+        'password': 'password',
+        'headers': [{'key': 'TEST_KEY', 'value': 'TEST_VALUE'}],
+        })
+
+    def create_endpoint(self, endpoint_msg):
+        self.mon_port = self.get_http_port()
+        endpoint = mon_params.HTTPEndpoint(
+                logger,
+                self._loop,
+                "127.0.0.1",
+                self.endpoint_msg,
+                )
+        # For each creation, update the descriptor as well
+        endpoint_msg.port = self.mon_port
+
+        return endpoint
+
+    def create_mon_param(self):
+        return mon_params.MonitoringParam(logger, self.mon_param_msg)
+
+    def get_app(self):
+        class PingStatsHandler(tornado.web.RequestHandler):
+            def get(this):
+                test_header = this.request.headers.get('TEST_KEY')
+                if test_header is None or test_header != 'TEST_VALUE':
+                    this.set_status(401)
+                    this.finish()
+                    return None
+
+                auth_header = this.request.headers.get('Authorization')
+                if auth_header is None or not auth_header.startswith('Basic '):
+                    this.set_status(401)
+                    this.set_header('WWW-Authenticate', 'Basic realm=Restricted')
+                    this._transforms = []
+                    this.finish()
+                    return None
+
+                auth_header = auth_header.encode('ascii')
+                auth_decoded = base64.decodestring(auth_header[6:]).decode('ascii')
+                login, password = auth_decoded.split(':', 2)
+                login = login.encode('ascii')
+                password = password.encode('ascii')
+                is_auth = (login == b"admin" and password == b"password")
+
+                if not is_auth:
+                    this.set_status(401)
+                    this.set_header('WWW-Authenticate', 'Basic realm=Restricted')
+                    this._transforms = []
+                    this.finish()
+                    return None
+
+                this.write(self.ping_response)
+
+        return tornado.web.Application([
+            (self.ping_path, PingStatsHandler),
+            ])
+
+    def test_value_convert(self):
+        float_con = mon_params.ValueConverter("DECIMAL")
+        int_con = mon_params.ValueConverter("INT")
+        text_con = mon_params.ValueConverter("STRING")
+
+        a = float_con.convert("1.23")
+        self.assertEqual(a, 1.23)
+
+        a = float_con.convert(1)
+        self.assertEqual(a, float(1))
+
+        t = text_con.convert(1.23)
+        self.assertEqual(t, "1.23")
+
+        t = text_con.convert("asdf")
+        self.assertEqual(t, "asdf")
+
+        i = int_con.convert(1.23)
+        self.assertEqual(i, 1)
+
+    def test_json_key_value_querier(self):
+        kv_querier = mon_params.JsonKeyValueQuerier(logger, "ping-request-tx-count")
+        value = kv_querier.query(tornado.escape.json_encode(self.ping_response))
+        self.assertEqual(value, 5)
+
+    def test_json_path_value_querier(self):
+        kv_querier = mon_params.JsonPathValueQuerier(logger, '$.ping-request-tx-count')
+        value = kv_querier.query(tornado.escape.json_encode(self.ping_response))
+        self.assertEqual(value, 5)
+
+    def test_object_path_value_querier(self):
+        kv_querier = mon_params.ObjectPathValueQuerier(logger, "$.*['ping-request-tx-count']")
+        value = kv_querier.query(tornado.escape.json_encode(self.ping_response))
+        self.assertEqual(value, 5)
+
+    def test_endpoint(self):
+        @asyncio.coroutine
+        def run_test():
+            endpoint = self.create_endpoint(self.endpoint_msg)
+            resp = yield from endpoint.poll()
+            resp_json = tornado.escape.json_decode(resp)
+            self.assertEqual(resp_json["ping-request-tx-count"], 5)
+            self.assertEqual(resp_json["ping-response-rx-count"], 10)
+
+        self._loop.run_until_complete(
+                asyncio.wait_for(run_test(), 10, loop=self._loop)
+                )
+
+    def test_mon_param(self):
+        a = self.create_mon_param()
+        a.extract_value_from_response(tornado.escape.json_encode(self.ping_response))
+        self.assertEqual(a.current_value, 5)
+        self.assertEqual(a.msg.value_integer, 5)
+
+    def test_endpoint_poller(self):
+        endpoint = self.create_endpoint(self.endpoint_msg)
+        mon_param = self.create_mon_param()
+        poller = mon_params.EndpointMonParamsPoller(
+                logger, self._loop, endpoint, [mon_param],
+                )
+        poller.start()
+
+        self._loop.run_until_complete(asyncio.sleep(1, loop=self._loop))
+        self.assertEqual(mon_param.current_value, 5)
+
+        poller.stop()
+
+    def test_params_controller(self):
+        new_port = self.get_http_port()
+        # Update port after new port is initialized
+        self.endpoint_msg.port = new_port
+        ctrl = mon_params.VnfMonitoringParamsController(
+                logger, self._loop, "1", "127.0.0.1", 
+                [self.endpoint_msg], [self.mon_param_msg],
+                )
+        ctrl.start()
+
+        self._loop.run_until_complete(asyncio.sleep(1, loop=self._loop))
+
+        ctrl.stop()
+
+        self.assertEqual(1, len(ctrl.mon_params))
+        mon_param = ctrl.mon_params[0]
+        self.assertEqual(mon_param.current_value, 5)
+
+
+class AsyncioTornadoHttpsTest(tornado.testing.AsyncHTTPSTestCase):
+    def setUp(self):
+        self._loop = asyncio.get_event_loop()
+        super().setUp()
+
+    def get_new_ioloop(self):
+        return tornado.platform.asyncio.AsyncIOMainLoop()
+
+
+class MonParamsPingStatsHttpsTest(AsyncioTornadoHttpsTest):
+    ping_path = r"/api/v1/ping/stats"
+    ping_response = {
+            'ping-request-tx-count': 5,
+            'ping-response-rx-count': 10
+            }
+
+    mon_param_msg = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam()
+    mon_param_msg.from_dict({
+            'id': '1',
+            'name': 'ping-request-tx-count',
+            'json_query_method': "NAMEKEY",
+            'http_endpoint_ref': ping_path,
+            'value_type': "INT",
+            'description': 'no of ping requests',
+            'group_tag': 'Group1',
+            'widget_type': 'COUNTER',
+            'units': 'packets'
+            })
+
+    endpoint_msg = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_HttpEndpoint()
+    endpoint_msg.from_dict({
+        'path': ping_path,
+        'https': 'true',
+        'polling_interval_secs': 1,
+        'username': 'admin',
+        'password': 'password',
+        'headers': [{'key': 'TEST_KEY', 'value': 'TEST_VALUE'}],
+        })
+
+    def create_endpoint(self, endpoint_msg):
+        self.mon_port = self.get_http_port()
+        endpoint = mon_params.HTTPEndpoint(
+                logger,
+                self._loop,
+                "127.0.0.1",
+                self.endpoint_msg,
+                )
+        # For each creation, update the descriptor as well
+        endpoint_msg.port = self.mon_port
+
+        return endpoint
+
+    def create_mon_param(self):
+        return mon_params.MonitoringParam(logger, self.mon_param_msg)
+
+    def get_app(self):
+        class PingStatsHandler(tornado.web.RequestHandler):
+            def get(this):
+                test_header = this.request.headers.get('TEST_KEY')
+                if test_header is None or test_header != 'TEST_VALUE':
+                    this.set_status(401)
+                    this.finish()
+                    return None
+
+                auth_header = this.request.headers.get('Authorization')
+                if auth_header is None or not auth_header.startswith('Basic '):
+                    this.set_status(401)
+                    this.set_header('WWW-Authenticate', 'Basic realm=Restricted')
+                    this._transforms = []
+                    this.finish()
+                    return None
+
+                auth_header = auth_header.encode('ascii')
+                auth_decoded = base64.decodestring(auth_header[6:]).decode('ascii')
+                login, password = auth_decoded.split(':', 2)
+                login = login.encode('ascii')
+                password = password.encode('ascii')
+                is_auth = (login == b"admin" and password == b"password")
+
+                if not is_auth:
+                    this.set_status(401)
+                    this.set_header('WWW-Authenticate', 'Basic realm=Restricted')
+                    this._transforms = []
+                    this.finish()
+                    return None
+
+                this.write(self.ping_response)
+
+        return tornado.web.Application([
+            (self.ping_path, PingStatsHandler),
+            ])
+
+    def test_value_convert(self):
+        float_con = mon_params.ValueConverter("DECIMAL")
+        int_con = mon_params.ValueConverter("INT")
+        text_con = mon_params.ValueConverter("STRING")
+
+        a = float_con.convert("1.23")
+        self.assertEqual(a, 1.23)
+
+        a = float_con.convert(1)
+        self.assertEqual(a, float(1))
+
+        t = text_con.convert(1.23)
+        self.assertEqual(t, "1.23")
+
+        t = text_con.convert("asdf")
+        self.assertEqual(t, "asdf")
+
+        i = int_con.convert(1.23)
+        self.assertEqual(i, 1)
+
+    def test_json_key_value_querier(self):
+        kv_querier = mon_params.JsonKeyValueQuerier(logger, "ping-request-tx-count")
+        value = kv_querier.query(tornado.escape.json_encode(self.ping_response))
+        self.assertEqual(value, 5)
+
+    def test_endpoint(self):
+        @asyncio.coroutine
+        def run_test():
+            endpoint = self.create_endpoint(self.endpoint_msg)
+            resp = yield from endpoint.poll()
+            resp_json = tornado.escape.json_decode(resp)
+            self.assertEqual(resp_json["ping-request-tx-count"], 5)
+            self.assertEqual(resp_json["ping-response-rx-count"], 10)
+
+        self._loop.run_until_complete(
+                asyncio.wait_for(run_test(), 10, loop=self._loop)
+                )
+
+    def test_mon_param(self):
+        a = self.create_mon_param()
+        a.extract_value_from_response(tornado.escape.json_encode(self.ping_response))
+        self.assertEqual(a.current_value, 5)
+        self.assertEqual(a.msg.value_integer, 5)
+
+    def test_endpoint_poller(self):
+        endpoint = self.create_endpoint(self.endpoint_msg)
+        mon_param = self.create_mon_param()
+        poller = mon_params.EndpointMonParamsPoller(
+                logger, self._loop, endpoint, [mon_param],
+                )
+        poller.start()
+
+        self._loop.run_until_complete(asyncio.sleep(1, loop=self._loop))
+        self.assertEqual(mon_param.current_value, 5)
+
+        poller.stop()
+
+    def test_params_controller(self):
+        new_port = self.get_http_port()
+        # Update port after new port is initialized
+        self.endpoint_msg.port = new_port
+        ctrl = mon_params.VnfMonitoringParamsController(
+                logger, self._loop, "1", "127.0.0.1", 
+                [self.endpoint_msg], [self.mon_param_msg],
+                )
+        ctrl.start()
+
+        self._loop.run_until_complete(asyncio.sleep(1, loop=self._loop))
+
+        ctrl.stop()
+
+        self.assertEqual(1, len(ctrl.mon_params))
+        mon_param = ctrl.mon_params[0]
+        self.assertEqual(mon_param.current_value, 5)
+
+
+class VRouterStatsTest(unittest.TestCase):
+    system_response = {
+        "system": {
+            "cpu": [
+                {
+                    "usage": 2.35,
+                    "cpu": "all"
+                },
+                {
+                    "usage": 5.35,
+                    "cpu": "1"
+                }
+            ]
+        }
+    }
+
+    def test_object_path_value_querier(self):
+        kv_querier = mon_params.ObjectPathValueQuerier(logger, "$.system.cpu[@.cpu is 'all'].usage")
+        value = kv_querier.query(tornado.escape.json_encode(self.system_response))
+        self.assertEqual(value, 2.35)
+
+
+class TrafsinkStatsTest(unittest.TestCase):
+    system_response = {
+       "rw-vnf-base-opdata:port-state": [
+         {
+           "ip": [
+             {
+               "address": "12.0.0.3/24"
+             }
+           ],
+           "rw-trafgen-data:trafgen-info": {
+             "src_l4_port": 1234,
+             "dst_l4_port": 5678,
+             "dst_ip_address": "192.168.1.1",
+             "tx_state": "Off",
+             "dst_mac_address": "00:00:00:00:00:00",
+             "tx_mode": "single-template",
+             "packet-count": 0,
+             "tx-cycles": 5478,
+             "tx_burst": 16,
+             "src_ip_address": "192.168.0.1",
+             "pkt_size": 64,
+             "src_mac_address": "fa:16:3e:07:b1:52",
+             "descr-string": "",
+             "tx_rate": 100
+           },
+           "counters": {
+             "input-errors": 0,
+             "output-bytes": 748,
+             "input-pause-xoff-pkts": 0,
+             "input-badcrc-pkts": 0,
+             "input-bytes": 62,
+             "rx-rate-mbps": 9576,
+             "output-pause-xoff-pkts": 0,
+             "input-missed-pkts": 0,
+             "input-packets": 1,
+             "output-errors": 0,
+             "tx-rate-mbps": 0,
+             "input-pause-xon-pkts": 0,
+             "output-pause-xon-pkts": 0,
+             "tx-rate-pps": 0,
+             "input-mcast-pkts": 0,
+             "rx-rate-pps": 0,
+             "output-packets": 6,
+             "input-nombuf-pkts": 0
+           },
+           "info": {
+             "numa-socket": 0,
+             "transmit-queues": 1,
+             "privatename": "eth_uio:pci=0000:00:04.0",
+             "duplex": "full-duplex",
+             "virtual-fabric": "No",
+             "link-state": "up",
+             "rte-port-id": 0,
+             "fastpath-instance": 1,
+             "id": 0,
+             "app-name": "rw_trafgen",
+             "speed": 10000,
+             "receive-queues": 1,
+             "descr-string": "",
+             "mac": "fa:16:3e:07:b1:52"
+           },
+           "portname": "trafsink_vnfd/cp0",
+           "queues": {
+             "rx-queue": [
+               {
+                 "packets": 1,
+                 "bytes-MB": 0,
+                 "qid": 0,
+                 "rate-mbps": 0,
+                 "rate-pps": 0
+               }
+             ],
+             "tx-queue": [
+               {
+                 "bytes-MB": 0,
+                 "packets": 6,
+                 "rate-pps": 0,
+                 "errors": 0,
+                 "qid": 0,
+                 "rate-mbps": 0
+               }
+             ]
+           }
+         }
+       ]
+     }
+
+    def test_object_path_value_querier(self):
+        kv_querier = mon_params.ObjectPathValueQuerier(logger, "$..*[@.portname is 'trafsink_vnfd/cp0'].counters.'rx-rate-mbps'")
+        value = kv_querier.query(tornado.escape.json_encode(self.system_response))
+        self.assertEqual(value, 9576)
+
+class IkeStatsTest(unittest.TestCase):
+    system_response = {
+      "rw-ipsec:ipsec-service-statistics": [
+    {
+      "name": "client1",
+      "statistics": {
+        "esp": {
+          "rx-bytes": 0,
+          "rx-packets": 0,
+          "tx-bytes": 0,
+          "tx-packets": 0
+        },
+        "rekey": {
+          "total": 3321,
+          "rate": 132,
+          "instantaneous-rate": 2
+        },
+        "state": {
+          "ike-sas": 10,
+          "threads-in-use": 5,
+          "swanctl-dir": "\/tmp\/strongswan4x3dni"
+        }
+      }
+    },
+    {
+      "name": "client0",
+      "statistics": {
+        "esp": {
+          "rx-bytes": 0,
+          "rx-packets": 0,
+          "tx-bytes": 0,
+          "tx-packets": 0
+        },
+        "rekey": {
+          "total": 3345,
+          "rate": 0,
+          "instantaneous-rate": 0
+        },
+        "state": {
+          "ike-sas": 50,
+          "threads-in-use": 5,
+          "swanctl-dir": "\/tmp\/strongswann21td3"
+        }
+      }
+    }
+  ]
+     }
+
+
+    def test_object_path_value_querier(self):
+        kv_querier = mon_params.ObjectPathValueQuerier(logger, "$..*[@.name is 'client1'].statistics.rekey.rate")
+        value = kv_querier.query(tornado.escape.json_encode(self.system_response))
+        self.assertEqual(value, 132)
+        kv_querier = mon_params.ObjectPathValueQuerier(logger, "$..*[@.name is 'client1'].statistics.state.'ike-sas'")
+        value = kv_querier.query(tornado.escape.json_encode(self.system_response))
+        self.assertEqual(value, 10)
+
+
+
+
+class PortLatencyTest(unittest.TestCase):
+    system_response = {
+  "rw-vnf-base-opdata:port-state": [
+    {
+      "info": {
+        "fastpath-instance": 1,
+        "duplex": "full-duplex",
+        "link-state": "up",
+        "lport-id": 81931,
+        "mtu": 1500,
+        "descr-string": "",
+        "transmit-queues": 1,
+        "mac": "fa:16:3e:c7:4a:b8",
+        "admin-state": "up",
+        "rte-port-id": 0,
+        "numa-socket": 0,
+        "app-name": "rw_trafgen",
+        "speed": 10000,
+        "virtual-fabric": "No",
+        "id": 0,
+        "receive-queues": 1,
+        "privatename": "eth_uio:pci=0000:00:04.0"
+      },
+      "rw-trafgen-data:trafgen-info": {
+        "maximum-latency": 124412,
+        "latency-distribution": [
+          {
+            "range-end": 100,
+            "range-start": 0,
+            "packets": 0
+          },
+          {
+            "range-end": 200,
+            "range-start": 101,
+            "packets": 0
+          },
+          {
+            "range-end": 300,
+            "range-start": 201,
+            "packets": 0
+          },
+          {
+            "range-end": 400,
+            "range-start": 301,
+            "packets": 0
+          },
+          {
+            "range-end": 500,
+            "range-start": 401,
+            "packets": 0
+          },
+          {
+            "range-end": 600,
+            "range-start": 501,
+            "packets": 0
+          },
+          {
+            "range-end": 700,
+            "range-start": 601,
+            "packets": 0
+          },
+          {
+            "range-end": 800,
+            "range-start": 701,
+            "packets": 0
+          },
+          {
+            "range-end": 900,
+            "range-start": 801,
+            "packets": 0
+          },
+          {
+            "range-end": 1000,
+            "range-start": 901,
+            "packets": 0
+          },
+          {
+            "range-end": 1100,
+            "range-start": 1001,
+            "packets": 0
+          },
+          {
+            "range-end": 1200,
+            "range-start": 1101,
+            "packets": 0
+          },
+          {
+            "range-end": 1300,
+            "range-start": 1201,
+            "packets": 0
+          },
+          {
+            "range-end": 1400,
+            "range-start": 1301,
+            "packets": 0
+          },
+          {
+            "range-end": 1500,
+            "range-start": 1401,
+            "packets": 0
+          },
+          {
+            "range-end": 0,
+            "range-start": 1501,
+            "packets": 1513641
+          }
+        ],
+        "descr-string": "",
+        "tx_mode": "range-template",
+        "minimum-latency": 1928,
+        "pkt_size": 512,
+        "tx_rate": 100,
+        "tx-cycles": 35206,
+        "src_ip_address": "12.0.0.3",
+        "src_l4_port": 10000,
+        "dst_ip_address": "12.0.0.2",
+        "mean-deviation": 4500,
+        "queue": [
+          {
+            "maximum": 124412,
+            "num-packets": 1513641,
+            "average": 12112,
+            "mean-deviation": 4500,
+            "qid": 0,
+            "minimum": 1928
+          }
+        ],
+        "packet-count": 0,
+        "average-latency": 12112,
+        "dst_l4_port": 5678,
+        "tx_state": "On",
+        "tx_burst": 16
+      },
+      "counters": {
+        "tx-rate-pps": 139630,
+        "rx-rate-mbps": 232,
+        "tx-rate-mbps": 589,
+        "output-packets": 49285239,
+        "input-missed-pkts": 0,
+        "output-errors": 0,
+        "input-nombuf-pkts": 0,
+        "input-errors": 0,
+        "input-mcast-pkts": 0,
+        "output-bytes": 26022584932,
+        "input-packets": 22537250,
+        "input-bytes": 11899650400,
+        "rx-rate-pps": 55158
+      },
+      "portname": "trafgencp0",
+      "ip": [
+        {
+          "address": "12.0.0.3\/24"
+        }
+      ],
+      "queues": {
+        "rx-queue": [
+          {
+            "packets": 22537250,
+            "bytes-MB": 95197,
+            "rate-mbps": 232,
+            "qid": 0,
+            "rate-pps": 55158
+          }
+        ],
+        "tx-queue": [
+          {
+            "bytes-MB": 208180,
+            "packets": 49285239,
+            "errors": 0,
+            "rate-mbps": 589,
+            "qid": 0,
+            "rate-pps": 139630
+          }
+        ]
+      },
+      "extended-stats": {
+        "xstats": [
+          {
+            "name": "rx_good_packets",
+            "value": 22555470
+          },
+          {
+            "name": "tx_good_packets",
+            "value": 49337664
+          },
+          {
+            "name": "rx_good_bytes",
+            "value": 11458161160
+          },
+          {
+            "name": "tx_good_bytes",
+            "value": 25063512052
+          },
+          {
+            "name": "rx_errors",
+            "value": 0
+          },
+          {
+            "name": "tx_errors",
+            "value": 0
+          },
+          {
+            "name": "rx_mbuf_allocation_errors",
+            "value": 0
+          },
+          {
+            "name": "rx_q0_packets",
+            "value": 22555470
+          },
+          {
+            "name": "rx_q0_bytes",
+            "value": 11458161160
+          },
+          {
+            "name": "rx_q0_errors",
+            "value": 0
+          },
+          {
+            "name": "tx_q0_packets",
+            "value": 49337664
+          },
+          {
+            "name": "tx_q0_bytes",
+            "value": 25063512052
+          },
+          {
+            "name": "rx_q0_good_packets",
+            "value": 22555470
+          },
+          {
+            "name": "rx_q0_good_bytes",
+            "value": 11458161160
+          },
+          {
+            "name": "rx_q0_multicast_packets",
+            "value": 0
+          },
+          {
+            "name": "rx_q0_broadcast_packets",
+            "value": 0
+          },
+          {
+            "name": "rx_q0_undersize_packets",
+            "value": 38
+          },
+          {
+            "name": "rx_q0_size_64_packets",
+            "value": 0
+          },
+          {
+            "name": "rx_q0_size_65_127_packets",
+            "value": 0
+          },
+          {
+            "name": "rx_q0_size_128_255_packets",
+            "value": 0
+          },
+          {
+            "name": "rx_q0_size_256_511_packets",
+            "value": 22555432
+          },
+          {
+            "name": "rx_q0_size_512_1023_packets",
+            "value": 0
+          },
+          {
+            "name": "rx_q0_size_1024_1517_packets",
+            "value": 0
+          },
+          {
+            "name": "rx_q0_size_1518_max_packets",
+            "value": 0
+          },
+          {
+            "name": "tx_q0_good_packets",
+            "value": 49337664
+          },
+          {
+            "name": "tx_q0_good_bytes",
+            "value": 25063512052
+          },
+          {
+            "name": "tx_q0_errors",
+            "value": 0
+          },
+          {
+            "name": "tx_q0_multicast_packets",
+            "value": 18
+          },
+          {
+            "name": "tx_q0_broadcast_packets",
+            "value": 11
+          },
+          {
+            "name": "tx_q0_undersize_packets",
+            "value": 40
+          },
+          {
+            "name": "tx_q0_size_64_packets",
+            "value": 0
+          },
+          {
+            "name": "tx_q0_size_65_127_packets",
+            "value": 5
+          },
+          {
+            "name": "tx_q0_size_128_255_packets",
+            "value": 2
+          },
+          {
+            "name": "tx_q0_size_256_511_packets",
+            "value": 49337617
+          },
+          {
+            "name": "tx_q0_size_512_1023_packets",
+            "value": 0
+          },
+          {
+            "name": "tx_q0_size_1024_1517_packets",
+            "value": 0
+          },
+          {
+            "name": "tx_q0_size_1518_max_packets",
+            "value": 0
+          }
+        ]
+      },
+      "lacp-info": {
+        "state": {
+          "distributing": "On",
+          "active": "Off",
+          "collecting": "On"
+        },
+        "counters": {
+          "marker": {
+            "rx": 0,
+            "tx": 0,
+            "errors": 0,
+            "nobuf": 0
+          },
+          "lacppdu": {
+            "rx": 0,
+            "tx": 0,
+            "errors": 0,
+            "nobuf": 0
+          }
+        }
+      }
+    }
+  ]
+    }
+
+
+    def test_object_path_value_querier(self):
+          kv_querier = mon_params.ObjectPathValueQuerier(logger, "$..*[@.portname is 'trafgencp0'].'rw-trafgen-data:trafgen-info'.pkt_size")
+          value = kv_querier.query(tornado.escape.json_encode(self.system_response))
+          self.assertEqual(value, 512)
+          kv_querier = mon_params.ObjectPathValueQuerier(logger, "$..*[@.portname is 'trafgencp0'].'rw-trafgen-data:trafgen-info'.'average-latency'")
+          value = kv_querier.query(tornado.escape.json_encode(self.system_response))
+          self.assertEqual(value, 12112)
+
+
+def main(argv=sys.argv[1:]):
+
+    # The unittest framework requires a program name, so use the name of this
+    # file instead (we do not want to have to pass a fake program name to main
+    # when this is called from the interpreter).
+    unittest.main(
+            argv=[__file__] + argv,
+            testRunner=xmlrunner.XMLTestRunner(output=os.environ["RIFT_MODULE_TEST"])
+            )
+
+if __name__ == '__main__':
+    main()
+