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