4 # Copyright 2016 RIFT.IO Inc
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
26 import tornado
.platform
.asyncio
27 import tornado
.testing
32 import rift
.tasklets
.rwmonparam
.vnfr_core
as mon_params
35 from gi
.repository
import VnfrYang
37 logger
= logging
.getLogger("mon_params_test.py")
40 class AsyncioTornadoTest(tornado
.testing
.AsyncHTTPTestCase
):
42 self
._loop
= asyncio
.get_event_loop()
45 def get_new_ioloop(self
):
46 return tornado
.platform
.asyncio
.AsyncIOMainLoop()
49 class MonParamsPingStatsTest(AsyncioTornadoTest
):
50 ping_path
= r
"/api/v1/ping/stats"
52 'ping-request-tx-count': 5,
53 'ping-response-rx-count': 10
56 mon_param_msg
= VnfrYang
.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam()
57 mon_param_msg
.from_dict({
59 'name': 'ping-request-tx-count',
60 'json_query_method': "NAMEKEY",
61 'http_endpoint_ref': ping_path
,
63 'description': 'no of ping requests',
64 'group_tag': 'Group1',
65 'widget_type': 'COUNTER',
69 endpoint_msg
= VnfrYang
.YangData_Vnfr_VnfrCatalog_Vnfr_HttpEndpoint()
70 endpoint_msg
.from_dict({
72 'polling_interval_secs': 1,
74 'password': 'password',
75 'headers': [{'key': 'TEST_KEY', 'value': 'TEST_VALUE'}],
78 def create_endpoint(self
, endpoint_msg
):
79 self
.mon_port
= self
.get_http_port()
80 endpoint
= mon_params
.HTTPEndpoint(
86 # For each creation, update the descriptor as well
87 endpoint_msg
.port
= self
.mon_port
91 def create_mon_param(self
):
92 return mon_params
.MonitoringParam(logger
, self
.mon_param_msg
)
95 class PingStatsHandler(tornado
.web
.RequestHandler
):
97 test_header
= this
.request
.headers
.get('TEST_KEY')
98 if test_header
is None or test_header
!= 'TEST_VALUE':
103 auth_header
= this
.request
.headers
.get('Authorization')
104 if auth_header
is None or not auth_header
.startswith('Basic '):
106 this
.set_header('WWW-Authenticate', 'Basic realm=Restricted')
107 this
._transforms
= []
111 auth_header
= auth_header
.encode('ascii')
112 auth_decoded
= base64
.decodestring(auth_header
[6:]).decode('ascii')
113 login
, password
= auth_decoded
.split(':', 2)
114 login
= login
.encode('ascii')
115 password
= password
.encode('ascii')
116 is_auth
= (login
== b
"admin" and password
== b
"password")
120 this
.set_header('WWW-Authenticate', 'Basic realm=Restricted')
121 this
._transforms
= []
125 this
.write(self
.ping_response
)
127 return tornado
.web
.Application([
128 (self
.ping_path
, PingStatsHandler
),
131 def test_value_convert(self
):
132 float_con
= mon_params
.ValueConverter("DECIMAL")
133 int_con
= mon_params
.ValueConverter("INT")
134 text_con
= mon_params
.ValueConverter("STRING")
136 a
= float_con
.convert("1.23")
137 self
.assertEqual(a
, 1.23)
139 a
= float_con
.convert(1)
140 self
.assertEqual(a
, float(1))
142 t
= text_con
.convert(1.23)
143 self
.assertEqual(t
, "1.23")
145 t
= text_con
.convert("asdf")
146 self
.assertEqual(t
, "asdf")
148 i
= int_con
.convert(1.23)
149 self
.assertEqual(i
, 1)
151 def test_json_key_value_querier(self
):
152 kv_querier
= mon_params
.JsonKeyValueQuerier(logger
, "ping-request-tx-count")
153 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.ping_response
))
154 self
.assertEqual(value
, 5)
156 def test_json_path_value_querier(self
):
157 kv_querier
= mon_params
.JsonPathValueQuerier(logger
, '$.ping-request-tx-count')
158 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.ping_response
))
159 self
.assertEqual(value
, 5)
161 def test_object_path_value_querier(self
):
162 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$.*['ping-request-tx-count']")
163 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.ping_response
))
164 self
.assertEqual(value
, 5)
166 def test_endpoint(self
):
169 endpoint
= self
.create_endpoint(self
.endpoint_msg
)
170 resp
= yield from endpoint
.poll()
171 resp_json
= tornado
.escape
.json_decode(resp
)
172 self
.assertEqual(resp_json
["ping-request-tx-count"], 5)
173 self
.assertEqual(resp_json
["ping-response-rx-count"], 10)
175 self
._loop
.run_until_complete(
176 asyncio
.wait_for(run_test(), 10, loop
=self
._loop
)
179 def test_mon_param(self
):
180 a
= self
.create_mon_param()
181 a
.extract_value_from_response(tornado
.escape
.json_encode(self
.ping_response
))
182 self
.assertEqual(a
.current_value
, 5)
183 self
.assertEqual(a
.msg
.value_integer
, 5)
185 def test_endpoint_poller(self
):
186 endpoint
= self
.create_endpoint(self
.endpoint_msg
)
187 mon_param
= self
.create_mon_param()
188 poller
= mon_params
.EndpointMonParamsPoller(
189 logger
, self
._loop
, endpoint
, [mon_param
],
193 self
._loop
.run_until_complete(asyncio
.sleep(1, loop
=self
._loop
))
194 self
.assertEqual(mon_param
.current_value
, 5)
198 def test_params_controller(self
):
199 new_port
= self
.get_http_port()
200 # Update port after new port is initialized
201 self
.endpoint_msg
.port
= new_port
202 ctrl
= mon_params
.VnfMonitoringParamsController(
203 logger
, self
._loop
, "1", "127.0.0.1",
204 [self
.endpoint_msg
], [self
.mon_param_msg
],
208 self
._loop
.run_until_complete(asyncio
.sleep(1, loop
=self
._loop
))
212 self
.assertEqual(1, len(ctrl
.mon_params
))
213 mon_param
= ctrl
.mon_params
[0]
214 self
.assertEqual(mon_param
.current_value
, 5)
217 class AsyncioTornadoHttpsTest(tornado
.testing
.AsyncHTTPSTestCase
):
219 self
._loop
= asyncio
.get_event_loop()
222 def get_new_ioloop(self
):
223 return tornado
.platform
.asyncio
.AsyncIOMainLoop()
226 class MonParamsPingStatsHttpsTest(AsyncioTornadoHttpsTest
):
227 ping_path
= r
"/api/v1/ping/stats"
229 'ping-request-tx-count': 5,
230 'ping-response-rx-count': 10
233 mon_param_msg
= VnfrYang
.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam()
234 mon_param_msg
.from_dict({
236 'name': 'ping-request-tx-count',
237 'json_query_method': "NAMEKEY",
238 'http_endpoint_ref': ping_path
,
240 'description': 'no of ping requests',
241 'group_tag': 'Group1',
242 'widget_type': 'COUNTER',
246 endpoint_msg
= VnfrYang
.YangData_Vnfr_VnfrCatalog_Vnfr_HttpEndpoint()
247 endpoint_msg
.from_dict({
250 'polling_interval_secs': 1,
252 'password': 'password',
253 'headers': [{'key': 'TEST_KEY', 'value': 'TEST_VALUE'}],
256 def create_endpoint(self
, endpoint_msg
):
257 self
.mon_port
= self
.get_http_port()
258 endpoint
= mon_params
.HTTPEndpoint(
264 # For each creation, update the descriptor as well
265 endpoint_msg
.port
= self
.mon_port
269 def create_mon_param(self
):
270 return mon_params
.MonitoringParam(logger
, self
.mon_param_msg
)
273 class PingStatsHandler(tornado
.web
.RequestHandler
):
275 test_header
= this
.request
.headers
.get('TEST_KEY')
276 if test_header
is None or test_header
!= 'TEST_VALUE':
281 auth_header
= this
.request
.headers
.get('Authorization')
282 if auth_header
is None or not auth_header
.startswith('Basic '):
284 this
.set_header('WWW-Authenticate', 'Basic realm=Restricted')
285 this
._transforms
= []
289 auth_header
= auth_header
.encode('ascii')
290 auth_decoded
= base64
.decodestring(auth_header
[6:]).decode('ascii')
291 login
, password
= auth_decoded
.split(':', 2)
292 login
= login
.encode('ascii')
293 password
= password
.encode('ascii')
294 is_auth
= (login
== b
"admin" and password
== b
"password")
298 this
.set_header('WWW-Authenticate', 'Basic realm=Restricted')
299 this
._transforms
= []
303 this
.write(self
.ping_response
)
305 return tornado
.web
.Application([
306 (self
.ping_path
, PingStatsHandler
),
309 def test_value_convert(self
):
310 float_con
= mon_params
.ValueConverter("DECIMAL")
311 int_con
= mon_params
.ValueConverter("INT")
312 text_con
= mon_params
.ValueConverter("STRING")
314 a
= float_con
.convert("1.23")
315 self
.assertEqual(a
, 1.23)
317 a
= float_con
.convert(1)
318 self
.assertEqual(a
, float(1))
320 t
= text_con
.convert(1.23)
321 self
.assertEqual(t
, "1.23")
323 t
= text_con
.convert("asdf")
324 self
.assertEqual(t
, "asdf")
326 i
= int_con
.convert(1.23)
327 self
.assertEqual(i
, 1)
329 def test_json_key_value_querier(self
):
330 kv_querier
= mon_params
.JsonKeyValueQuerier(logger
, "ping-request-tx-count")
331 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.ping_response
))
332 self
.assertEqual(value
, 5)
334 def test_endpoint(self
):
337 endpoint
= self
.create_endpoint(self
.endpoint_msg
)
338 resp
= yield from endpoint
.poll()
339 resp_json
= tornado
.escape
.json_decode(resp
)
340 self
.assertEqual(resp_json
["ping-request-tx-count"], 5)
341 self
.assertEqual(resp_json
["ping-response-rx-count"], 10)
343 self
._loop
.run_until_complete(
344 asyncio
.wait_for(run_test(), 10, loop
=self
._loop
)
347 def test_mon_param(self
):
348 a
= self
.create_mon_param()
349 a
.extract_value_from_response(tornado
.escape
.json_encode(self
.ping_response
))
350 self
.assertEqual(a
.current_value
, 5)
351 self
.assertEqual(a
.msg
.value_integer
, 5)
353 def test_endpoint_poller(self
):
354 endpoint
= self
.create_endpoint(self
.endpoint_msg
)
355 mon_param
= self
.create_mon_param()
356 poller
= mon_params
.EndpointMonParamsPoller(
357 logger
, self
._loop
, endpoint
, [mon_param
],
361 self
._loop
.run_until_complete(asyncio
.sleep(1, loop
=self
._loop
))
362 self
.assertEqual(mon_param
.current_value
, 5)
366 def test_params_controller(self
):
367 new_port
= self
.get_http_port()
368 # Update port after new port is initialized
369 self
.endpoint_msg
.port
= new_port
370 ctrl
= mon_params
.VnfMonitoringParamsController(
371 logger
, self
._loop
, "1", "127.0.0.1",
372 [self
.endpoint_msg
], [self
.mon_param_msg
],
376 self
._loop
.run_until_complete(asyncio
.sleep(1, loop
=self
._loop
))
380 self
.assertEqual(1, len(ctrl
.mon_params
))
381 mon_param
= ctrl
.mon_params
[0]
382 self
.assertEqual(mon_param
.current_value
, 5)
385 class VRouterStatsTest(unittest
.TestCase
):
401 def test_object_path_value_querier(self
):
402 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$.system.cpu[@.cpu is 'all'].usage")
403 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
404 self
.assertEqual(value
, 2.35)
407 class TrafsinkStatsTest(unittest
.TestCase
):
409 "rw-vnf-base-opdata:port-state": [
413 "address": "12.0.0.3/24"
416 "rw-trafgen-data:trafgen-info": {
419 "dst_ip_address": "192.168.1.1",
421 "dst_mac_address": "00:00:00:00:00:00",
422 "tx_mode": "single-template",
426 "src_ip_address": "192.168.0.1",
428 "src_mac_address": "fa:16:3e:07:b1:52",
435 "input-pause-xoff-pkts": 0,
436 "input-badcrc-pkts": 0,
438 "rx-rate-mbps": 9576,
439 "output-pause-xoff-pkts": 0,
440 "input-missed-pkts": 0,
444 "input-pause-xon-pkts": 0,
445 "output-pause-xon-pkts": 0,
447 "input-mcast-pkts": 0,
450 "input-nombuf-pkts": 0
454 "transmit-queues": 1,
455 "privatename": "eth_uio:pci=0000:00:04.0",
456 "duplex": "full-duplex",
457 "virtual-fabric": "No",
460 "fastpath-instance": 1,
462 "app-name": "rw_trafgen",
466 "mac": "fa:16:3e:07:b1:52"
468 "portname": "trafsink_vnfd/cp0",
494 def test_object_path_value_querier(self
):
495 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$..*[@.portname is 'trafsink_vnfd/cp0'].counters.'rx-rate-mbps'")
496 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
497 self
.assertEqual(value
, 9576)
499 class IkeStatsTest(unittest
.TestCase
):
501 "rw-ipsec:ipsec-service-statistics": [
514 "instantaneous-rate": 2
519 "swanctl-dir": "\/tmp\/strongswan4x3dni"
535 "instantaneous-rate": 0
540 "swanctl-dir": "\/tmp\/strongswann21td3"
548 def test_object_path_value_querier(self
):
549 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$..*[@.name is 'client1'].statistics.rekey.rate")
550 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
551 self
.assertEqual(value
, 132)
552 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$..*[@.name is 'client1'].statistics.state.'ike-sas'")
553 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
554 self
.assertEqual(value
, 10)
559 class PortLatencyTest(unittest
.TestCase
):
561 "rw-vnf-base-opdata:port-state": [
564 "fastpath-instance": 1,
565 "duplex": "full-duplex",
570 "transmit-queues": 1,
571 "mac": "fa:16:3e:c7:4a:b8",
575 "app-name": "rw_trafgen",
577 "virtual-fabric": "No",
580 "privatename": "eth_uio:pci=0000:00:04.0"
582 "rw-trafgen-data:trafgen-info": {
583 "maximum-latency": 124412,
584 "latency-distribution": [
667 "tx_mode": "range-template",
668 "minimum-latency": 1928,
672 "src_ip_address": "12.0.0.3",
673 "src_l4_port": 10000,
674 "dst_ip_address": "12.0.0.2",
675 "mean-deviation": 4500,
679 "num-packets": 1513641,
681 "mean-deviation": 4500,
687 "average-latency": 12112,
693 "tx-rate-pps": 139630,
696 "output-packets": 49285239,
697 "input-missed-pkts": 0,
699 "input-nombuf-pkts": 0,
701 "input-mcast-pkts": 0,
702 "output-bytes": 26022584932,
703 "input-packets": 22537250,
704 "input-bytes": 11899650400,
707 "portname": "trafgencp0",
710 "address": "12.0.0.3\/24"
737 "name": "rx_good_packets",
741 "name": "tx_good_packets",
745 "name": "rx_good_bytes",
749 "name": "tx_good_bytes",
761 "name": "rx_mbuf_allocation_errors",
765 "name": "rx_q0_packets",
769 "name": "rx_q0_bytes",
773 "name": "rx_q0_errors",
777 "name": "tx_q0_packets",
781 "name": "tx_q0_bytes",
785 "name": "rx_q0_good_packets",
789 "name": "rx_q0_good_bytes",
793 "name": "rx_q0_multicast_packets",
797 "name": "rx_q0_broadcast_packets",
801 "name": "rx_q0_undersize_packets",
805 "name": "rx_q0_size_64_packets",
809 "name": "rx_q0_size_65_127_packets",
813 "name": "rx_q0_size_128_255_packets",
817 "name": "rx_q0_size_256_511_packets",
821 "name": "rx_q0_size_512_1023_packets",
825 "name": "rx_q0_size_1024_1517_packets",
829 "name": "rx_q0_size_1518_max_packets",
833 "name": "tx_q0_good_packets",
837 "name": "tx_q0_good_bytes",
841 "name": "tx_q0_errors",
845 "name": "tx_q0_multicast_packets",
849 "name": "tx_q0_broadcast_packets",
853 "name": "tx_q0_undersize_packets",
857 "name": "tx_q0_size_64_packets",
861 "name": "tx_q0_size_65_127_packets",
865 "name": "tx_q0_size_128_255_packets",
869 "name": "tx_q0_size_256_511_packets",
873 "name": "tx_q0_size_512_1023_packets",
877 "name": "tx_q0_size_1024_1517_packets",
881 "name": "tx_q0_size_1518_max_packets",
888 "distributing": "On",
912 def test_object_path_value_querier(self
):
913 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$..*[@.portname is 'trafgencp0'].'rw-trafgen-data:trafgen-info'.pkt_size")
914 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
915 self
.assertEqual(value
, 512)
916 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$..*[@.portname is 'trafgencp0'].'rw-trafgen-data:trafgen-info'.'average-latency'")
917 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
918 self
.assertEqual(value
, 12112)
921 def main(argv
=sys
.argv
[1:]):
923 # The unittest framework requires a program name, so use the name of this
924 # file instead (we do not want to have to pass a fake program name to main
925 # when this is called from the interpreter).
927 argv
=[__file__
] + argv
,
928 testRunner
=xmlrunner
.XMLTestRunner(output
=os
.environ
["RIFT_MODULE_TEST"])
931 if __name__
== '__main__':