c00ca11f7347b934545fc586b2067657580c2606
[osm/SO.git] / rwlaunchpad / plugins / rwautoscaler / test / utest_autoscaler_dts.py
1 #!/usr/bin/env python3
2
3 #
4 # Copyright 2016 RIFT.IO Inc
5 #
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
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
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.
17 #
18
19 import argparse
20 import asyncio
21 import os
22 import sys
23 import unittest
24 import random
25
26 import xmlrunner
27 import unittest.mock as mock
28
29 import rift.test.dts
30 import rift.tasklets.rwautoscaler.engine as engine
31 import gi
32 gi.require_version('RwDtsYang', '1.0')
33 from gi.repository import (
34 RwNsrYang,
35 NsrYang,
36 NsdYang,
37 RwLaunchpadYang as launchpadyang,
38 RwVnfrYang,
39 RwVnfdYang,
40 RwNsdYang,
41 VnfrYang
42 )
43
44
45 ScalingCriteria = NsdYang.YangData_Nsd_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy_ScalingCriteria
46 ScalingPolicy = NsdYang.YangData_Nsd_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy
47
48
49 class MockDelegate(engine.ScalingCriteria.Delegate):
50 def __init__(self):
51 self.scale_in_called = 0
52 self.scale_out_called = 0
53
54 def scale_in(self, name, val):
55 print ("=============================================")
56 print ("Scaling IN")
57 print ("=============================================")
58 self.scale_in_called += 1
59
60 def scale_out(self, name, val):
61 print ("=============================================")
62 print ("Scaling OUT")
63 print ("=============================================")
64 self.scale_out_called += 1
65
66
67 class MockStore():
68 def __init__(self, aggregation_type="AVERAGE", legacy=False):
69 self.aggregation_type = aggregation_type
70 self.legacy = legacy
71 self.threshold_time = 3
72
73 def __call__(self):
74 store = mock.MagicMock()
75
76 mock_vnfd = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd.from_dict({
77 'id': "1",
78 'monitoring_param': [
79 {'description': 'no of ping requests',
80 'group_tag': 'Group1',
81 'http_endpoint_ref': 'api/v1/ping/stats',
82 'id': '1',
83 'json_query_method': 'NAMEKEY',
84 'name': 'ping-request-tx-count',
85 'units': 'packets',
86 'value_type': 'INT',
87 'widget_type': 'COUNTER'},
88 {'description': 'no of ping responses',
89 'group_tag': 'Group1',
90 'http_endpoint_ref': 'api/v1/ping/stats',
91 'id': '2',
92 'json_query_method': 'NAMEKEY',
93 'name': 'ping-response-rx-count',
94 'units': 'packets',
95 'value_type': 'INT',
96 'widget_type': 'COUNTER'}],
97 })
98
99 store.get_vnfd = mock.MagicMock(return_value=mock_vnfd)
100
101 mock_vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict({'id': '1'})
102 mock_vnfr.vnfd = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vnfd.from_dict({'id': '1'})
103
104 store.get_vnfr = mock.MagicMock(return_value=mock_vnfr)
105
106 mock_nsr = RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr.from_dict({
107 'ns_instance_config_ref': "1",
108 'name_ref': "Foo",
109 'nsd_ref': '1',
110 'config_status': 'configured',
111 'constituent_vnfr_ref': [{'vnfr_id': mock_vnfr.id}],
112 })
113
114 store.get_nsr = mock.MagicMock(return_value=mock_nsr)
115 store.nsr = [mock_nsr]
116
117 monp_cfg = [{'aggregation_type': self.aggregation_type,
118 'id': '1',
119 'name': 'ping-request-tx-count',
120 'value_type': 'INT',
121 'vnfd_monitoring_param': [
122 {'vnfd_id_ref': '1',
123 'vnfd_monitoring_param_ref': '1'},
124 {'vnfd_id_ref': '1',
125 'vnfd_monitoring_param_ref': '2'}]
126 },
127 {'aggregation_type': self.aggregation_type,
128 'id': '2',
129 'name': 'ping-request-tx-count',
130 'value_type': 'INT',
131 'vnfd_monitoring_param': [
132 {'vnfd_id_ref': '1',
133 'vnfd_monitoring_param_ref': '1'},
134 {'vnfd_id_ref': '1',
135 'vnfd_monitoring_param_ref': '2'}]
136 }]
137
138 scale_in_val = 100
139 scale_out_val = 200
140
141 mock_nsd = RwNsdYang.YangData_Nsd_NsdCatalog_Nsd.from_dict({
142 'id': '1',
143 'monitoring_param': (monp_cfg if not self.legacy else []),
144 'constituent_vnfd': [{'member_vnf_index': 1,
145 'start_by_default': True,
146 'vnfd_id_ref': '1'},
147 {'member_vnf_index': 2,
148 'start_by_default': True,
149 'vnfd_id_ref': '1'}],
150 'scaling_group_descriptor': [{
151 "name": "http",
152 "vnfd_member": [{
153 'member_vnf_index_ref': 1,
154 }],
155 "scaling_policy": [{
156 "scaling_type": "automatic",
157 "enabled": True,
158 "threshold_time": self.threshold_time,
159 "cooldown_time": 60,
160 "scale_out_operation_type": "AND",
161 "scale_in_operation_type": "AND",
162 "scaling_criteria": [{
163 "name": "1",
164 "scale_in_threshold": scale_in_val,
165 "scale_out_threshold": scale_out_val,
166 "ns_monitoring_param_ref": "1"
167 },
168 {
169 "name": "2",
170 "scale_in_threshold": scale_in_val,
171 "scale_out_threshold": scale_out_val,
172 "ns_monitoring_param_ref": "2"
173 }]
174 }]
175 }]
176 })
177
178 store.get_nsd = mock.MagicMock(return_value=mock_nsd)
179
180 return store
181
182
183 class AutoscalarDtsTestCase(rift.test.dts.AbstractDTSTest):
184 @classmethod
185 def configure_schema(cls):
186 return launchpadyang.get_schema()
187
188 @classmethod
189 def configure_timeout(cls):
190 return 240
191
192 def configure_test(self, loop, test_id):
193 self.log.debug("STARTING - %s", test_id)
194 self.tinfo = self.new_tinfo(str(test_id))
195 self.dts = rift.tasklets.DTS(self.tinfo, self.schema, self.loop)
196
197 self.tinfo_sub = self.new_tinfo(str(test_id) + "_sub")
198 self.dts_sub = rift.tasklets.DTS(self.tinfo_sub, self.schema, self.loop)
199
200 self.mock_store = MockStore()
201
202 def tearDown(self):
203 super().tearDown()
204
205 @asyncio.coroutine
206 def _populate_mock_values(self, criterias, nsr_id, floor, ceil):
207 # Mock publish
208 # Verify Scale in AND operator
209 NsMonParam = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_MonitoringParam
210
211 publisher = rift.test.dts.DescriptorPublisher(self.log, self.dts, self.loop)
212
213 for criteria in criterias:
214 monp_id = criteria.ns_monitoring_param_ref
215 w_xpath = "D,/nsr:ns-instance-opdata/nsr:nsr"
216 w_xpath = w_xpath + "[nsr:ns-instance-config-ref='{}']/nsr:monitoring-param".format(nsr_id)
217 xpath = w_xpath + "[nsr:id ='{}']".format(monp_id)
218
219 for i in range(self.mock_store.threshold_time + 1):
220 value = random.randint(floor, ceil)
221
222 monp = NsMonParam.from_dict({
223 'id': monp_id,
224 'value_integer': value,
225 'nsd_mon_param_ref': monp_id})
226
227 yield from publisher.publish(w_xpath, xpath, monp)
228 yield from asyncio.sleep(1)
229
230 @rift.test.dts.async_test
231 def test_scale_in(self):
232 store = self.mock_store()
233
234 # CFG
235 floor, ceil = 0, 100
236 nsr_id = store.get_nsr().ns_instance_config_ref
237 policy_cfg = store.get_nsd().scaling_group_descriptor[0].scaling_policy[0]
238 scaling_name = store.get_nsd().scaling_group_descriptor[0].name
239
240
241 def make_policy():
242 policy = engine.ScalingPolicy(
243 self.log, self.dts, self.loop,
244 store.get_nsr().ns_instance_config_ref, store.get_nsd().id,
245 scaling_name, policy_cfg, store, delegate=mock_delegate)
246
247 return policy
248
249 @asyncio.coroutine
250 def scale_out(policy):
251 yield from self._populate_mock_values(policy.scaling_criteria, nsr_id, 200, 300)
252 # HACK TO RESET THE COOLING TIME
253 policy._last_triggered_time = 0
254
255 # Test 1: Scale in shouldn't be called, unless a scale-out happens
256 mock_delegate = MockDelegate()
257 policy = make_policy()
258 yield from policy.register()
259 yield from self._populate_mock_values(policy.scaling_criteria, nsr_id, floor, ceil)
260 assert mock_delegate.scale_in_called == 0
261
262 # Test 2: AND operation
263 yield from scale_out(policy)
264 yield from self._populate_mock_values(policy.scaling_criteria, nsr_id, floor, ceil)
265 assert mock_delegate.scale_in_called == 1
266
267 # Test 3: AND operation failure
268 mock_delegate = MockDelegate()
269 policy = make_policy()
270 yield from policy.register()
271 yield from scale_out(policy)
272 yield from self._populate_mock_values([policy.scaling_criteria[0]], nsr_id, floor, ceil)
273 assert mock_delegate.scale_in_called == 0
274
275
276 # Test 4: OR operation
277 mock_delegate = MockDelegate()
278 policy = make_policy()
279 policy_cfg.scale_in_operation_type = "OR"
280 yield from policy.register()
281 yield from scale_out(policy)
282 yield from self._populate_mock_values([policy.scaling_criteria[0]], nsr_id, floor, ceil)
283 assert mock_delegate.scale_in_called == 1
284
285 @rift.test.dts.async_test
286 def _test_scale_out(self):
287 """ Tests scale out
288
289 Asserts:
290 1. Scale out
291 2. Scale out doesn't happen during cooldown
292 3. AND operation
293 4. OR operation.
294 """
295 store = self.mock_store()
296
297 # CFG
298 floor, ceil = 200, 300
299 nsr_id = store.get_nsr().ns_instance_config_ref
300 policy_cfg = store.get_nsd().scaling_group_descriptor[0].scaling_policy[0]
301 scaling_name = store.get_nsd().scaling_group_descriptor[0].name
302
303
304 def make_policy():
305 policy = engine.ScalingPolicy(
306 self.log, self.dts, self.loop,
307 store.get_nsr().ns_instance_config_ref, store.get_nsd().id,
308 scaling_name, policy_cfg, store, delegate=mock_delegate)
309
310 return policy
311
312 # Test 1: Scale out should be called only when both the criteria are
313 # exceeding.
314 mock_delegate = MockDelegate()
315 policy = make_policy()
316 yield from policy.register()
317 yield from self._populate_mock_values(policy.scaling_criteria, nsr_id, floor, ceil)
318 assert mock_delegate.scale_out_called == 1
319
320 # Test 2: Assert if Scale out doesn't happen when only one exceeds
321 mock_delegate = MockDelegate()
322 policy = make_policy()
323 yield from policy.register()
324 yield from self._populate_mock_values([policy.scaling_criteria[0]], nsr_id, floor, ceil)
325 assert mock_delegate.scale_out_called == 0
326
327 # Test 3: OR operation
328 mock_delegate = MockDelegate()
329 policy_cfg.scale_out_operation_type = "OR"
330 policy = make_policy()
331 yield from policy.register()
332 yield from self._populate_mock_values([policy.scaling_criteria[0]], nsr_id, floor, ceil)
333 assert mock_delegate.scale_out_called == 1
334
335
336 def main():
337 runner = xmlrunner.XMLTestRunner(output=os.environ["RIFT_MODULE_TEST"])
338
339 parser = argparse.ArgumentParser()
340 parser.add_argument('-v', '--verbose', action='store_true')
341 parser.add_argument('-n', '--no-runner', action='store_true')
342 args, unittest_args = parser.parse_known_args()
343 if args.no_runner:
344 runner = None
345
346
347 unittest.main(testRunner=runner, argv=[sys.argv[0]] + unittest_args)
348
349 if __name__ == '__main__':
350 main()