5 # Copyright 2016 RIFT.IO Inc
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
11 # http://www.apache.org/licenses/LICENSE-2.0
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.
30 import unittest
.mock
as mock
33 import rift
.tasklets
.rwautoscaler
.engine
as engine
34 gi
.require_version('RwDtsYang', '1.0')
35 from gi
.repository
import (
38 ProjectNsdYang
as NsdYang
,
39 RwLaunchpadYang
as launchpadyang
,
41 RwProjectVnfdYang
as RwVnfdYang
,
42 RwProjectNsdYang
as RwNsdYang
,
45 gi
.require_version('RwKeyspec', '1.0')
46 from gi
.repository
.RwKeyspec
import quoted_key
49 ScalingCriteria
= NsdYang
.YangData_RwProject_Project_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy_ScalingCriteria
50 ScalingPolicy
= NsdYang
.YangData_RwProject_Project_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy
53 class MockDelegate(engine
.ScalingCriteria
.Delegate
):
55 self
.scale_in_called
= 0
56 self
.scale_out_called
= 0
58 def scale_in(self
, name
, val
):
59 print ("=============================================")
61 print ("=============================================")
62 self
.scale_in_called
+= 1
64 def scale_out(self
, name
, val
):
65 print ("=============================================")
67 print ("=============================================")
68 self
.scale_out_called
+= 1
72 def __init__(self
, aggregation_type
="AVERAGE", legacy
=False):
73 self
.aggregation_type
= aggregation_type
75 self
.threshold_time
= 2
78 store
= mock
.MagicMock()
80 mock_vnfd
= RwVnfdYang
.YangData_RwProject_Project_VnfdCatalog_Vnfd
.from_dict({
83 {'description': 'no of ping requests',
84 'group_tag': 'Group1',
85 'http_endpoint_ref': 'api/v1/ping/stats',
87 'json_query_method': 'NAMEKEY',
88 'name': 'ping-request-tx-count',
91 'widget_type': 'COUNTER'},
92 {'description': 'no of ping responses',
93 'group_tag': 'Group1',
94 'http_endpoint_ref': 'api/v1/ping/stats',
96 'json_query_method': 'NAMEKEY',
97 'name': 'ping-response-rx-count',
100 'widget_type': 'COUNTER'}],
103 store
.get_vnfd
= mock
.MagicMock(return_value
=mock_vnfd
)
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'})
108 store
.get_vnfr
= mock
.MagicMock(return_value
=mock_vnfr
)
110 mock_nsr
= RwNsrYang
.YangData_RwProject_Project_NsInstanceOpdata_Nsr
.from_dict({
111 'ns_instance_config_ref': "1",
114 'config_status': 'configured',
115 'constituent_vnfr_ref': [{'vnfr_id': mock_vnfr
.id}],
118 store
.get_nsr
= mock
.MagicMock(return_value
=mock_nsr
)
119 store
.nsr
= [mock_nsr
]
121 monp_cfg
= [{'aggregation_type': self
.aggregation_type
,
123 'name': 'ping-request-tx-count',
125 'vnfd_monitoring_param': [
127 'vnfd_monitoring_param_ref': '1'},
129 'vnfd_monitoring_param_ref': '2'}]
131 {'aggregation_type': self
.aggregation_type
,
133 'name': 'ping-request-tx-count',
135 'vnfd_monitoring_param': [
137 'vnfd_monitoring_param_ref': '1'},
139 'vnfd_monitoring_param_ref': '2'}]
145 mock_nsd
= RwNsdYang
.YangData_RwProject_Project_NsdCatalog_Nsd
.from_dict({
149 'monitoring_param': (monp_cfg
if not self
.legacy
else []),
150 'constituent_vnfd': [{'member_vnf_index': 1,
151 'start_by_default': True,
153 {'member_vnf_index': 2,
154 'start_by_default': True,
155 'vnfd_id_ref': '1'}],
156 'scaling_group_descriptor': [{
159 'member_vnf_index_ref': 1,
162 "scaling_type": "automatic",
164 "threshold_time": self
.threshold_time
,
166 "scale_out_operation_type": "AND",
167 "scale_in_operation_type": "AND",
168 "scaling_criteria": [{
170 "scale_in_threshold": scale_in_val
,
171 "scale_out_threshold": scale_out_val
,
172 "ns_monitoring_param_ref": "1"
176 "scale_in_threshold": scale_in_val
,
177 "scale_out_threshold": scale_out_val
,
178 "ns_monitoring_param_ref": "2"
184 store
.get_nsd
= mock
.MagicMock(return_value
=mock_nsd
)
189 class AutoscalarDtsTestCase(rift
.test
.dts
.AbstractDTSTest
):
191 def configure_schema(cls
):
192 return launchpadyang
.get_schema()
195 def configure_timeout(cls
):
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
)
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
)
206 self
.mock_store
= MockStore()
212 def _populate_mock_values(self
, criterias
, nsr_id
, floor
, ceil
):
214 # Verify Scale in AND operator
215 NsMonParam
= NsrYang
.YangData_RwProject_Project_NsInstanceOpdata_Nsr_MonitoringParam
217 publisher
= rift
.test
.dts
.DescriptorPublisher(self
.log
, self
.dts
, self
.loop
)
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
))
225 for i
in range(self
.mock_store
.threshold_time
+ 2):
226 value
= random
.randint(floor
, ceil
)
228 monp
= NsMonParam
.from_dict({
230 'value_integer': value
,
231 'nsd_mon_param_ref': monp_id
})
233 yield from publisher
.publish(w_xpath
, xpath
, monp
)
234 yield from asyncio
.sleep(1)
236 @rift.test
.dts
.async_test
237 def test_scale_in(self
):
238 store
= self
.mock_store()
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
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
)
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
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
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
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
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
291 @rift.test
.dts
.async_test
292 def test_scale_out(self
):
297 2. Scale out doesn't happen during cooldown
301 store
= self
.mock_store()
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
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
)
318 # Test 1: Scale out should be called only when both the criteria are
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
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
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
343 logging
.basicConfig(format
='TEST %(message)s')
344 runner
= xmlrunner
.XMLTestRunner(output
=os
.environ
["RIFT_MODULE_TEST"])
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()
353 # Set the global logging level
354 logging
.getLogger().setLevel(logging
.DEBUG
if args
.verbose
else logging
.ERROR
)
357 unittest
.main(testRunner
=runner
, argv
=[sys
.argv
[0]] + unittest_args
)
359 if __name__
== '__main__':