X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;ds=inline;f=rwlaunchpad%2Fplugins%2Frwautoscaler%2Ftest%2Futest_autoscaler_dts.py;fp=rwlaunchpad%2Fplugins%2Frwautoscaler%2Ftest%2Futest_autoscaler_dts.py;h=78342cee120cdc2db01bc5c8338be55f27f7f78c;hb=6f07e6f33f751ab4ffe624f6037f887b243bece2;hp=0000000000000000000000000000000000000000;hpb=72a563886272088feb7cb52e4aafbe6d2c580ff9;p=osm%2FSO.git diff --git a/rwlaunchpad/plugins/rwautoscaler/test/utest_autoscaler_dts.py b/rwlaunchpad/plugins/rwautoscaler/test/utest_autoscaler_dts.py new file mode 100644 index 00000000..78342cee --- /dev/null +++ b/rwlaunchpad/plugins/rwautoscaler/test/utest_autoscaler_dts.py @@ -0,0 +1,350 @@ +#!/usr/bin/env python3 + +# +# Copyright 2016 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import argparse +import asyncio +import os +import sys +import unittest +import random + +import xmlrunner +import unittest.mock as mock + +import rift.test.dts +import rift.tasklets.rwautoscaler.engine as engine +import gi +gi.require_version('RwDtsYang', '1.0') +from gi.repository import ( + RwNsrYang, + NsrYang, + NsdYang, + RwLaunchpadYang as launchpadyang, + RwVnfrYang, + RwVnfdYang, + RwNsdYang + ) + + +ScalingCriteria = NsdYang.YangData_Nsd_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy_ScalingCriteria +ScalingPolicy = NsdYang.YangData_Nsd_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy + + +class MockDelegate(engine.ScalingCriteria.Delegate): + def __init__(self): + self.scale_in_called = 0 + self.scale_out_called = 0 + + def scale_in(self, name, val): + print ("=============================================") + print ("Scaling IN") + print ("=============================================") + self.scale_in_called += 1 + + def scale_out(self, name, val): + print ("=============================================") + print ("Scaling OUT") + print ("=============================================") + self.scale_out_called += 1 + + +class MockStore(): + def __init__(self, aggregation_type="AVERAGE", legacy=False): + self.aggregation_type = aggregation_type + self.legacy = legacy + self.threshold_time = 3 + + def __call__(self): + store = mock.MagicMock() + + mock_vnfd = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd.from_dict({ + 'id': "1", + 'monitoring_param': [ + {'description': 'no of ping requests', + 'group_tag': 'Group1', + 'http_endpoint_ref': 'api/v1/ping/stats', + 'id': '1', + 'json_query_method': 'NAMEKEY', + 'name': 'ping-request-tx-count', + 'units': 'packets', + 'value_type': 'INT', + 'widget_type': 'COUNTER'}, + {'description': 'no of ping responses', + 'group_tag': 'Group1', + 'http_endpoint_ref': 'api/v1/ping/stats', + 'id': '2', + 'json_query_method': 'NAMEKEY', + 'name': 'ping-response-rx-count', + 'units': 'packets', + 'value_type': 'INT', + 'widget_type': 'COUNTER'}], + }) + + store.get_vnfd = mock.MagicMock(return_value=mock_vnfd) + + mock_vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict({ + 'id': '1', + 'vnfd_ref': '1', + }) + store.get_vnfr = mock.MagicMock(return_value=mock_vnfr) + + mock_nsr = RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr.from_dict({ + 'ns_instance_config_ref': "1", + 'name_ref': "Foo", + 'nsd_ref': '1', + 'config_status': 'configured', + 'constituent_vnfr_ref': [{'vnfr_id': mock_vnfr.id}], + }) + + store.get_nsr = mock.MagicMock(return_value=mock_nsr) + store.nsr = [mock_nsr] + + monp_cfg = [{'aggregation_type': self.aggregation_type, + 'id': '1', + 'name': 'ping-request-tx-count', + 'value_type': 'INT', + 'vnfd_monitoring_param': [ + {'vnfd_id_ref': '1', + 'vnfd_monitoring_param_ref': '1'}, + {'vnfd_id_ref': '1', + 'vnfd_monitoring_param_ref': '2'}] + }, + {'aggregation_type': self.aggregation_type, + 'id': '2', + 'name': 'ping-request-tx-count', + 'value_type': 'INT', + 'vnfd_monitoring_param': [ + {'vnfd_id_ref': '1', + 'vnfd_monitoring_param_ref': '1'}, + {'vnfd_id_ref': '1', + 'vnfd_monitoring_param_ref': '2'}] + }] + + scale_in_val = 100 + scale_out_val = 200 + + mock_nsd = RwNsdYang.YangData_Nsd_NsdCatalog_Nsd.from_dict({ + 'id': '1', + 'monitoring_param': (monp_cfg if not self.legacy else []), + 'constituent_vnfd': [{'member_vnf_index': 1, + 'start_by_default': True, + 'vnfd_id_ref': '1'}, + {'member_vnf_index': 2, + 'start_by_default': True, + 'vnfd_id_ref': '1'}], + 'scaling_group_descriptor': [{ + "name": "http", + "vnfd_member": [{ + 'member_vnf_index_ref': 1, + }], + "scaling_policy": [{ + "scaling_type": "automatic", + "enabled": True, + "threshold_time": self.threshold_time, + "cooldown_time": 60, + "scale_out_operation_type": "AND", + "scale_in_operation_type": "AND", + "scaling_criteria": [{ + "name": "1", + "scale_in_threshold": scale_in_val, + "scale_out_threshold": scale_out_val, + "ns_monitoring_param_ref": "1" + }, + { + "name": "2", + "scale_in_threshold": scale_in_val, + "scale_out_threshold": scale_out_val, + "ns_monitoring_param_ref": "2" + }] + }] + }] + }) + + store.get_nsd = mock.MagicMock(return_value=mock_nsd) + + return store + + +class AutoscalarDtsTestCase(rift.test.dts.AbstractDTSTest): + @classmethod + def configure_schema(cls): + return launchpadyang.get_schema() + + @classmethod + def configure_timeout(cls): + return 240 + + def configure_test(self, loop, test_id): + self.log.debug("STARTING - %s", test_id) + self.tinfo = self.new_tinfo(str(test_id)) + self.dts = rift.tasklets.DTS(self.tinfo, self.schema, self.loop) + + self.tinfo_sub = self.new_tinfo(str(test_id) + "_sub") + self.dts_sub = rift.tasklets.DTS(self.tinfo_sub, self.schema, self.loop) + + self.mock_store = MockStore() + + def tearDown(self): + super().tearDown() + + @asyncio.coroutine + def _populate_mock_values(self, criterias, nsr_id, floor, ceil): + # Mock publish + # Verify Scale in AND operator + NsMonParam = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_MonitoringParam + + publisher = rift.test.dts.DescriptorPublisher(self.log, self.dts, self.loop) + + for criteria in criterias: + monp_id = criteria.ns_monitoring_param_ref + w_xpath = "D,/nsr:ns-instance-opdata/nsr:nsr" + w_xpath = w_xpath + "[nsr:ns-instance-config-ref='{}']/nsr:monitoring-param".format(nsr_id) + xpath = w_xpath + "[nsr:id ='{}']".format(monp_id) + + for i in range(self.mock_store.threshold_time + 1): + value = random.randint(floor, ceil) + + monp = NsMonParam.from_dict({ + 'id': monp_id, + 'value_integer': value, + 'nsd_mon_param_ref': monp_id}) + + yield from publisher.publish(w_xpath, xpath, monp) + yield from asyncio.sleep(1) + + @rift.test.dts.async_test + def test_scale_in(self): + store = self.mock_store() + + # CFG + floor, ceil = 0, 100 + nsr_id = store.get_nsr().ns_instance_config_ref + policy_cfg = store.get_nsd().scaling_group_descriptor[0].scaling_policy[0] + scaling_name = store.get_nsd().scaling_group_descriptor[0].name + + + def make_policy(): + policy = engine.ScalingPolicy( + self.log, self.dts, self.loop, + store.get_nsr().ns_instance_config_ref, store.get_nsd().id, + scaling_name, policy_cfg, store, delegate=mock_delegate) + + return policy + + @asyncio.coroutine + def scale_out(policy): + yield from self._populate_mock_values(policy.scaling_criteria, nsr_id, 200, 300) + # HACK TO RESET THE COOLING TIME + policy._last_triggered_time = 0 + + # Test 1: Scale in shouldn't be called, unless a scale-out happens + mock_delegate = MockDelegate() + policy = make_policy() + yield from policy.register() + yield from self._populate_mock_values(policy.scaling_criteria, nsr_id, floor, ceil) + assert mock_delegate.scale_in_called == 0 + + # Test 2: AND operation + yield from scale_out(policy) + yield from self._populate_mock_values(policy.scaling_criteria, nsr_id, floor, ceil) + assert mock_delegate.scale_in_called == 1 + + # Test 3: AND operation failure + mock_delegate = MockDelegate() + policy = make_policy() + yield from policy.register() + yield from scale_out(policy) + yield from self._populate_mock_values([policy.scaling_criteria[0]], nsr_id, floor, ceil) + assert mock_delegate.scale_in_called == 0 + + + # Test 4: OR operation + mock_delegate = MockDelegate() + policy = make_policy() + policy_cfg.scale_in_operation_type = "OR" + yield from policy.register() + yield from scale_out(policy) + yield from self._populate_mock_values([policy.scaling_criteria[0]], nsr_id, floor, ceil) + assert mock_delegate.scale_in_called == 1 + + @rift.test.dts.async_test + def _test_scale_out(self): + """ Tests scale out + + Asserts: + 1. Scale out + 2. Scale out doesn't happen during cooldown + 3. AND operation + 4. OR operation. + """ + store = self.mock_store() + + # CFG + floor, ceil = 200, 300 + nsr_id = store.get_nsr().ns_instance_config_ref + policy_cfg = store.get_nsd().scaling_group_descriptor[0].scaling_policy[0] + scaling_name = store.get_nsd().scaling_group_descriptor[0].name + + + def make_policy(): + policy = engine.ScalingPolicy( + self.log, self.dts, self.loop, + store.get_nsr().ns_instance_config_ref, store.get_nsd().id, + scaling_name, policy_cfg, store, delegate=mock_delegate) + + return policy + + # Test 1: Scale out should be called only when both the criteria are + # exceeding. + mock_delegate = MockDelegate() + policy = make_policy() + yield from policy.register() + yield from self._populate_mock_values(policy.scaling_criteria, nsr_id, floor, ceil) + assert mock_delegate.scale_out_called == 1 + + # Test 2: Assert if Scale out doesn't happen when only one exceeds + mock_delegate = MockDelegate() + policy = make_policy() + yield from policy.register() + yield from self._populate_mock_values([policy.scaling_criteria[0]], nsr_id, floor, ceil) + assert mock_delegate.scale_out_called == 0 + + # Test 3: OR operation + mock_delegate = MockDelegate() + policy_cfg.scale_out_operation_type = "OR" + policy = make_policy() + yield from policy.register() + yield from self._populate_mock_values([policy.scaling_criteria[0]], nsr_id, floor, ceil) + assert mock_delegate.scale_out_called == 1 + + +def main(): + runner = xmlrunner.XMLTestRunner(output=os.environ["RIFT_MODULE_TEST"]) + + parser = argparse.ArgumentParser() + parser.add_argument('-v', '--verbose', action='store_true') + parser.add_argument('-n', '--no-runner', action='store_true') + args, unittest_args = parser.parse_known_args() + if args.no_runner: + runner = None + + + unittest.main(testRunner=runner, argv=[sys.argv[0]] + unittest_args) + +if __name__ == '__main__': + main()