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
31 import xmltodict
, json
33 import rift
.tasklets
.rwmonparam
.vnfr_core
as mon_params
36 from gi
.repository
import VnfrYang
38 logger
= logging
.getLogger("mon_params_test.py")
41 class AsyncioTornadoTest(tornado
.testing
.AsyncHTTPTestCase
):
43 self
._loop
= asyncio
.get_event_loop()
46 def get_new_ioloop(self
):
47 return tornado
.platform
.asyncio
.AsyncIOMainLoop()
50 class MonParamsPingStatsTest(AsyncioTornadoTest
):
51 ping_path
= r
"/api/v1/ping/stats"
53 'ping-request-tx-count': 5,
54 'ping-response-rx-count': 10
57 mon_param_msg
= VnfrYang
.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam()
58 mon_param_msg
.from_dict({
60 'name': 'ping-request-tx-count',
61 'json_query_method': "NAMEKEY",
62 'http_endpoint_ref': ping_path
,
64 'description': 'no of ping requests',
65 'group_tag': 'Group1',
66 'widget_type': 'COUNTER',
70 endpoint_msg
= VnfrYang
.YangData_Vnfr_VnfrCatalog_Vnfr_HttpEndpoint()
71 endpoint_msg
.from_dict({
73 'polling_interval_secs': 1,
75 'password': 'password',
76 'headers': [{'key': 'TEST_KEY', 'value': 'TEST_VALUE'}],
79 def create_endpoint(self
, endpoint_msg
):
80 self
.mon_port
= self
.get_http_port()
81 endpoint
= mon_params
.HTTPEndpoint(
87 # For each creation, update the descriptor as well
88 endpoint_msg
.port
= self
.mon_port
92 def create_mon_param(self
):
93 return mon_params
.MonitoringParam(logger
, self
.mon_param_msg
)
96 class PingStatsHandler(tornado
.web
.RequestHandler
):
98 test_header
= this
.request
.headers
.get('TEST_KEY')
99 if test_header
is None or test_header
!= 'TEST_VALUE':
104 auth_header
= this
.request
.headers
.get('Authorization')
105 if auth_header
is None or not auth_header
.startswith('Basic '):
107 this
.set_header('WWW-Authenticate', 'Basic realm=Restricted')
108 this
._transforms
= []
112 auth_header
= auth_header
.encode('ascii')
113 auth_decoded
= base64
.decodestring(auth_header
[6:]).decode('ascii')
114 login
, password
= auth_decoded
.split(':', 2)
115 login
= login
.encode('ascii')
116 password
= password
.encode('ascii')
117 is_auth
= (login
== b
"admin" and password
== b
"password")
121 this
.set_header('WWW-Authenticate', 'Basic realm=Restricted')
122 this
._transforms
= []
126 this
.write(self
.ping_response
)
128 return tornado
.web
.Application([
129 (self
.ping_path
, PingStatsHandler
),
132 def test_value_convert(self
):
133 float_con
= mon_params
.ValueConverter("DECIMAL")
134 int_con
= mon_params
.ValueConverter("INT")
135 text_con
= mon_params
.ValueConverter("STRING")
137 a
= float_con
.convert("1.23")
138 self
.assertEqual(a
, 1.23)
140 a
= float_con
.convert(1)
141 self
.assertEqual(a
, float(1))
143 t
= text_con
.convert(1.23)
144 self
.assertEqual(t
, "1.23")
146 t
= text_con
.convert("asdf")
147 self
.assertEqual(t
, "asdf")
149 i
= int_con
.convert(1.23)
150 self
.assertEqual(i
, 1)
152 def test_json_key_value_querier(self
):
153 kv_querier
= mon_params
.JsonKeyValueQuerier(logger
, "ping-request-tx-count")
154 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.ping_response
))
155 self
.assertEqual(value
, 5)
157 def test_json_path_value_querier(self
):
158 kv_querier
= mon_params
.JsonPathValueQuerier(logger
, '$.ping-request-tx-count')
159 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.ping_response
))
160 self
.assertEqual(value
, 5)
162 def test_object_path_value_querier(self
):
163 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$.*['ping-request-tx-count']")
164 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.ping_response
))
165 self
.assertEqual(value
, 5)
167 def test_endpoint(self
):
170 endpoint
= self
.create_endpoint(self
.endpoint_msg
)
171 resp
= yield from endpoint
.poll()
172 resp_json
= tornado
.escape
.json_decode(resp
)
173 self
.assertEqual(resp_json
["ping-request-tx-count"], 5)
174 self
.assertEqual(resp_json
["ping-response-rx-count"], 10)
176 self
._loop
.run_until_complete(
177 asyncio
.wait_for(run_test(), 10, loop
=self
._loop
)
180 def test_mon_param(self
):
181 a
= self
.create_mon_param()
182 a
.extract_value_from_response(tornado
.escape
.json_encode(self
.ping_response
))
183 self
.assertEqual(a
.current_value
, 5)
184 self
.assertEqual(a
.msg
.value_integer
, 5)
186 def test_endpoint_poller(self
):
187 endpoint
= self
.create_endpoint(self
.endpoint_msg
)
188 mon_param
= self
.create_mon_param()
189 poller
= mon_params
.EndpointMonParamsPoller(
190 logger
, self
._loop
, endpoint
, [mon_param
],
194 self
._loop
.run_until_complete(asyncio
.sleep(1, loop
=self
._loop
))
195 self
.assertEqual(mon_param
.current_value
, 5)
199 def test_params_controller(self
):
200 new_port
= self
.get_http_port()
201 # Update port after new port is initialized
202 self
.endpoint_msg
.port
= new_port
203 ctrl
= mon_params
.VnfMonitoringParamsController(
204 logger
, self
._loop
, "1", "127.0.0.1",
205 [self
.endpoint_msg
], [self
.mon_param_msg
],
209 self
._loop
.run_until_complete(asyncio
.sleep(1, loop
=self
._loop
))
213 self
.assertEqual(1, len(ctrl
.mon_params
))
214 mon_param
= ctrl
.mon_params
[0]
215 self
.assertEqual(mon_param
.current_value
, 5)
218 class AsyncioTornadoHttpsTest(tornado
.testing
.AsyncHTTPSTestCase
):
220 self
._loop
= asyncio
.get_event_loop()
223 def get_new_ioloop(self
):
224 return tornado
.platform
.asyncio
.AsyncIOMainLoop()
227 class MonParamsPingStatsHttpsTest(AsyncioTornadoHttpsTest
):
228 ping_path
= r
"/api/v1/ping/stats"
230 'ping-request-tx-count': 5,
231 'ping-response-rx-count': 10
234 mon_param_msg
= VnfrYang
.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam()
235 mon_param_msg
.from_dict({
237 'name': 'ping-request-tx-count',
238 'json_query_method': "NAMEKEY",
239 'http_endpoint_ref': ping_path
,
241 'description': 'no of ping requests',
242 'group_tag': 'Group1',
243 'widget_type': 'COUNTER',
247 endpoint_msg
= VnfrYang
.YangData_Vnfr_VnfrCatalog_Vnfr_HttpEndpoint()
248 endpoint_msg
.from_dict({
251 'polling_interval_secs': 1,
253 'password': 'password',
254 'headers': [{'key': 'TEST_KEY', 'value': 'TEST_VALUE'}],
257 def create_endpoint(self
, endpoint_msg
):
258 self
.mon_port
= self
.get_http_port()
259 endpoint
= mon_params
.HTTPEndpoint(
265 # For each creation, update the descriptor as well
266 endpoint_msg
.port
= self
.mon_port
270 def create_mon_param(self
):
271 return mon_params
.MonitoringParam(logger
, self
.mon_param_msg
)
274 class PingStatsHandler(tornado
.web
.RequestHandler
):
276 test_header
= this
.request
.headers
.get('TEST_KEY')
277 if test_header
is None or test_header
!= 'TEST_VALUE':
282 auth_header
= this
.request
.headers
.get('Authorization')
283 if auth_header
is None or not auth_header
.startswith('Basic '):
285 this
.set_header('WWW-Authenticate', 'Basic realm=Restricted')
286 this
._transforms
= []
290 auth_header
= auth_header
.encode('ascii')
291 auth_decoded
= base64
.decodestring(auth_header
[6:]).decode('ascii')
292 login
, password
= auth_decoded
.split(':', 2)
293 login
= login
.encode('ascii')
294 password
= password
.encode('ascii')
295 is_auth
= (login
== b
"admin" and password
== b
"password")
299 this
.set_header('WWW-Authenticate', 'Basic realm=Restricted')
300 this
._transforms
= []
304 this
.write(self
.ping_response
)
306 return tornado
.web
.Application([
307 (self
.ping_path
, PingStatsHandler
),
310 def test_value_convert(self
):
311 float_con
= mon_params
.ValueConverter("DECIMAL")
312 int_con
= mon_params
.ValueConverter("INT")
313 text_con
= mon_params
.ValueConverter("STRING")
315 a
= float_con
.convert("1.23")
316 self
.assertEqual(a
, 1.23)
318 a
= float_con
.convert(1)
319 self
.assertEqual(a
, float(1))
321 t
= text_con
.convert(1.23)
322 self
.assertEqual(t
, "1.23")
324 t
= text_con
.convert("asdf")
325 self
.assertEqual(t
, "asdf")
327 i
= int_con
.convert(1.23)
328 self
.assertEqual(i
, 1)
330 def test_json_key_value_querier(self
):
331 kv_querier
= mon_params
.JsonKeyValueQuerier(logger
, "ping-request-tx-count")
332 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.ping_response
))
333 self
.assertEqual(value
, 5)
335 def test_endpoint(self
):
338 endpoint
= self
.create_endpoint(self
.endpoint_msg
)
339 resp
= yield from endpoint
.poll()
340 resp_json
= tornado
.escape
.json_decode(resp
)
341 self
.assertEqual(resp_json
["ping-request-tx-count"], 5)
342 self
.assertEqual(resp_json
["ping-response-rx-count"], 10)
344 self
._loop
.run_until_complete(
345 asyncio
.wait_for(run_test(), 10, loop
=self
._loop
)
348 def test_mon_param(self
):
349 a
= self
.create_mon_param()
350 a
.extract_value_from_response(tornado
.escape
.json_encode(self
.ping_response
))
351 self
.assertEqual(a
.current_value
, 5)
352 self
.assertEqual(a
.msg
.value_integer
, 5)
354 def test_endpoint_poller(self
):
355 endpoint
= self
.create_endpoint(self
.endpoint_msg
)
356 mon_param
= self
.create_mon_param()
357 poller
= mon_params
.EndpointMonParamsPoller(
358 logger
, self
._loop
, endpoint
, [mon_param
],
362 self
._loop
.run_until_complete(asyncio
.sleep(1, loop
=self
._loop
))
363 self
.assertEqual(mon_param
.current_value
, 5)
367 def test_params_controller(self
):
368 new_port
= self
.get_http_port()
369 # Update port after new port is initialized
370 self
.endpoint_msg
.port
= new_port
371 ctrl
= mon_params
.VnfMonitoringParamsController(
372 logger
, self
._loop
, "1", "127.0.0.1",
373 [self
.endpoint_msg
], [self
.mon_param_msg
],
377 self
._loop
.run_until_complete(asyncio
.sleep(1, loop
=self
._loop
))
381 self
.assertEqual(1, len(ctrl
.mon_params
))
382 mon_param
= ctrl
.mon_params
[0]
383 self
.assertEqual(mon_param
.current_value
, 5)
386 class VRouterStatsTest(unittest
.TestCase
):
402 def test_object_path_value_querier(self
):
403 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$.system.cpu[@.cpu is 'all'].usage")
404 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
405 self
.assertEqual(value
, 2.35)
408 class TrafsinkStatsTest(unittest
.TestCase
):
410 "rw-vnf-base-opdata:port-state": [
414 "address": "12.0.0.3/24"
417 "rw-trafgen-data:trafgen-info": {
420 "dst_ip_address": "192.168.1.1",
422 "dst_mac_address": "00:00:00:00:00:00",
423 "tx_mode": "single-template",
427 "src_ip_address": "192.168.0.1",
429 "src_mac_address": "fa:16:3e:07:b1:52",
436 "input-pause-xoff-pkts": 0,
437 "input-badcrc-pkts": 0,
439 "rx-rate-mbps": 9576,
440 "output-pause-xoff-pkts": 0,
441 "input-missed-pkts": 0,
445 "input-pause-xon-pkts": 0,
446 "output-pause-xon-pkts": 0,
448 "input-mcast-pkts": 0,
451 "input-nombuf-pkts": 0
455 "transmit-queues": 1,
456 "privatename": "eth_uio:pci=0000:00:04.0",
457 "duplex": "full-duplex",
458 "virtual-fabric": "No",
461 "fastpath-instance": 1,
463 "app-name": "rw_trafgen",
467 "mac": "fa:16:3e:07:b1:52"
469 "portname": "trafsink_vnfd/cp0",
495 def test_object_path_value_querier(self
):
496 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$..*[@.portname is 'trafsink_vnfd/cp0'].counters.'rx-rate-mbps'")
497 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
498 self
.assertEqual(value
, 9576)
500 class IkeStatsTest(unittest
.TestCase
):
502 "rw-ipsec:ipsec-service-statistics": [
515 "instantaneous-rate": 2
520 "swanctl-dir": "\/tmp\/strongswan4x3dni"
536 "instantaneous-rate": 0
541 "swanctl-dir": "\/tmp\/strongswann21td3"
549 def test_object_path_value_querier(self
):
550 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$..*[@.name is 'client1'].statistics.rekey.rate")
551 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
552 self
.assertEqual(value
, 132)
553 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$..*[@.name is 'client1'].statistics.state.'ike-sas'")
554 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
555 self
.assertEqual(value
, 10)
560 class PortLatencyTest(unittest
.TestCase
):
562 "rw-vnf-base-opdata:port-state": [
565 "fastpath-instance": 1,
566 "duplex": "full-duplex",
571 "transmit-queues": 1,
572 "mac": "fa:16:3e:c7:4a:b8",
576 "app-name": "rw_trafgen",
578 "virtual-fabric": "No",
581 "privatename": "eth_uio:pci=0000:00:04.0"
583 "rw-trafgen-data:trafgen-info": {
584 "maximum-latency": 124412,
585 "latency-distribution": [
668 "tx_mode": "range-template",
669 "minimum-latency": 1928,
673 "src_ip_address": "12.0.0.3",
674 "src_l4_port": 10000,
675 "dst_ip_address": "12.0.0.2",
676 "mean-deviation": 4500,
680 "num-packets": 1513641,
682 "mean-deviation": 4500,
688 "average-latency": 12112,
694 "tx-rate-pps": 139630,
697 "output-packets": 49285239,
698 "input-missed-pkts": 0,
700 "input-nombuf-pkts": 0,
702 "input-mcast-pkts": 0,
703 "output-bytes": 26022584932,
704 "input-packets": 22537250,
705 "input-bytes": 11899650400,
708 "portname": "trafgencp0",
711 "address": "12.0.0.3\/24"
738 "name": "rx_good_packets",
742 "name": "tx_good_packets",
746 "name": "rx_good_bytes",
750 "name": "tx_good_bytes",
762 "name": "rx_mbuf_allocation_errors",
766 "name": "rx_q0_packets",
770 "name": "rx_q0_bytes",
774 "name": "rx_q0_errors",
778 "name": "tx_q0_packets",
782 "name": "tx_q0_bytes",
786 "name": "rx_q0_good_packets",
790 "name": "rx_q0_good_bytes",
794 "name": "rx_q0_multicast_packets",
798 "name": "rx_q0_broadcast_packets",
802 "name": "rx_q0_undersize_packets",
806 "name": "rx_q0_size_64_packets",
810 "name": "rx_q0_size_65_127_packets",
814 "name": "rx_q0_size_128_255_packets",
818 "name": "rx_q0_size_256_511_packets",
822 "name": "rx_q0_size_512_1023_packets",
826 "name": "rx_q0_size_1024_1517_packets",
830 "name": "rx_q0_size_1518_max_packets",
834 "name": "tx_q0_good_packets",
838 "name": "tx_q0_good_bytes",
842 "name": "tx_q0_errors",
846 "name": "tx_q0_multicast_packets",
850 "name": "tx_q0_broadcast_packets",
854 "name": "tx_q0_undersize_packets",
858 "name": "tx_q0_size_64_packets",
862 "name": "tx_q0_size_65_127_packets",
866 "name": "tx_q0_size_128_255_packets",
870 "name": "tx_q0_size_256_511_packets",
874 "name": "tx_q0_size_512_1023_packets",
878 "name": "tx_q0_size_1024_1517_packets",
882 "name": "tx_q0_size_1518_max_packets",
889 "distributing": "On",
913 def test_object_path_value_querier(self
):
914 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$..*[@.portname is 'trafgencp0'].'rw-trafgen-data:trafgen-info'.pkt_size")
915 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
916 self
.assertEqual(value
, 512)
917 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$..*[@.portname is 'trafgencp0'].'rw-trafgen-data:trafgen-info'.'average-latency'")
918 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
919 self
.assertEqual(value
, 12112)
923 class XMLReponseTest(unittest
.TestCase
):
924 xml_response
= "<response status='success'><result> <entry> <current>2</current> <vsys>1</vsys> <maximum>0</maximum> <throttled>0</throttled> </entry> </result></response>"
926 op
= xmltodict
.parse(xml_response
)
930 except Exception as e
:
931 print("Input is Not XML formatted")
934 def test_object_path_value_querier(self
):
935 kv_querier
= mon_params
.ObjectPathValueQuerier(logger
, "$.response.result.entry.current")
936 value
= kv_querier
.query(tornado
.escape
.json_encode(self
.system_response
))
937 self
.assertEqual(value
, '2')
939 def main(argv
=sys
.argv
[1:]):
941 # The unittest framework requires a program name, so use the name of this
942 # file instead (we do not want to have to pass a fake program name to main
943 # when this is called from the interpreter).
945 argv
=[__file__
] + argv
,
946 testRunner
=xmlrunner
.XMLTestRunner(output
=os
.environ
["RIFT_MODULE_TEST"])
949 if __name__
== '__main__':