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.
27 import unittest
.mock
as mock
30 import rift
.tasklets
.rwautoscaler
.engine
as engine
32 gi
.require_version('RwDtsYang', '1.0')
33 from gi
.repository
import (
37 RwLaunchpadYang
as launchpadyang
,
45 ScalingCriteria
= NsdYang
.YangData_Nsd_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy_ScalingCriteria
46 ScalingPolicy
= NsdYang
.YangData_Nsd_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy
49 class MockDelegate(engine
.ScalingCriteria
.Delegate
):
51 self
.scale_in_called
= 0
52 self
.scale_out_called
= 0
54 def scale_in(self
, name
, val
):
55 print ("=============================================")
57 print ("=============================================")
58 self
.scale_in_called
+= 1
60 def scale_out(self
, name
, val
):
61 print ("=============================================")
63 print ("=============================================")
64 self
.scale_out_called
+= 1
68 def __init__(self
, aggregation_type
="AVERAGE", legacy
=False):
69 self
.aggregation_type
= aggregation_type
71 self
.threshold_time
= 3
74 store
= mock
.MagicMock()
76 mock_vnfd
= RwVnfdYang
.YangData_Vnfd_VnfdCatalog_Vnfd
.from_dict({
79 {'description': 'no of ping requests',
80 'group_tag': 'Group1',
81 'http_endpoint_ref': 'api/v1/ping/stats',
83 'json_query_method': 'NAMEKEY',
84 'name': 'ping-request-tx-count',
87 'widget_type': 'COUNTER'},
88 {'description': 'no of ping responses',
89 'group_tag': 'Group1',
90 'http_endpoint_ref': 'api/v1/ping/stats',
92 'json_query_method': 'NAMEKEY',
93 'name': 'ping-response-rx-count',
96 'widget_type': 'COUNTER'}],
99 store
.get_vnfd
= mock
.MagicMock(return_value
=mock_vnfd
)
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'})
104 store
.get_vnfr
= mock
.MagicMock(return_value
=mock_vnfr
)
106 mock_nsr
= RwNsrYang
.YangData_Nsr_NsInstanceOpdata_Nsr
.from_dict({
107 'ns_instance_config_ref': "1",
110 'config_status': 'configured',
111 'constituent_vnfr_ref': [{'vnfr_id': mock_vnfr
.id}],
114 store
.get_nsr
= mock
.MagicMock(return_value
=mock_nsr
)
115 store
.nsr
= [mock_nsr
]
117 monp_cfg
= [{'aggregation_type': self
.aggregation_type
,
119 'name': 'ping-request-tx-count',
121 'vnfd_monitoring_param': [
123 'vnfd_monitoring_param_ref': '1'},
125 'vnfd_monitoring_param_ref': '2'}]
127 {'aggregation_type': self
.aggregation_type
,
129 'name': 'ping-request-tx-count',
131 'vnfd_monitoring_param': [
133 'vnfd_monitoring_param_ref': '1'},
135 'vnfd_monitoring_param_ref': '2'}]
141 mock_nsd
= RwNsdYang
.YangData_Nsd_NsdCatalog_Nsd
.from_dict({
143 'monitoring_param': (monp_cfg
if not self
.legacy
else []),
144 'constituent_vnfd': [{'member_vnf_index': 1,
145 'start_by_default': True,
147 {'member_vnf_index': 2,
148 'start_by_default': True,
149 'vnfd_id_ref': '1'}],
150 'scaling_group_descriptor': [{
153 'member_vnf_index_ref': 1,
156 "scaling_type": "automatic",
158 "threshold_time": self
.threshold_time
,
160 "scale_out_operation_type": "AND",
161 "scale_in_operation_type": "AND",
162 "scaling_criteria": [{
164 "scale_in_threshold": scale_in_val
,
165 "scale_out_threshold": scale_out_val
,
166 "ns_monitoring_param_ref": "1"
170 "scale_in_threshold": scale_in_val
,
171 "scale_out_threshold": scale_out_val
,
172 "ns_monitoring_param_ref": "2"
178 store
.get_nsd
= mock
.MagicMock(return_value
=mock_nsd
)
183 class AutoscalarDtsTestCase(rift
.test
.dts
.AbstractDTSTest
):
185 def configure_schema(cls
):
186 return launchpadyang
.get_schema()
189 def configure_timeout(cls
):
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
)
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
)
200 self
.mock_store
= MockStore()
206 def _populate_mock_values(self
, criterias
, nsr_id
, floor
, ceil
):
208 # Verify Scale in AND operator
209 NsMonParam
= NsrYang
.YangData_Nsr_NsInstanceOpdata_Nsr_MonitoringParam
211 publisher
= rift
.test
.dts
.DescriptorPublisher(self
.log
, self
.dts
, self
.loop
)
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
)
219 for i
in range(self
.mock_store
.threshold_time
+ 1):
220 value
= random
.randint(floor
, ceil
)
222 monp
= NsMonParam
.from_dict({
224 'value_integer': value
,
225 'nsd_mon_param_ref': monp_id
})
227 yield from publisher
.publish(w_xpath
, xpath
, monp
)
228 yield from asyncio
.sleep(1)
230 @rift.test
.dts
.async_test
231 def test_scale_in(self
):
232 store
= self
.mock_store()
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
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
)
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
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
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
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
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
285 @rift.test
.dts
.async_test
286 def _test_scale_out(self
):
291 2. Scale out doesn't happen during cooldown
295 store
= self
.mock_store()
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
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
)
312 # Test 1: Scale out should be called only when both the criteria are
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
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
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
337 runner
= xmlrunner
.XMLTestRunner(output
=os
.environ
["RIFT_MODULE_TEST"])
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()
347 unittest
.main(testRunner
=runner
, argv
=[sys
.argv
[0]] + unittest_args
)
349 if __name__
== '__main__':