def _testUncleanRestartWithApnAMBR(self, mock_meter_cls, mock_traffic_cls): """This test verifies all tests cases from _testUncleanRestart for APN AMBR configs. """ loop = asyncio.new_event_loop() qos_mgr = QosManager(MagicMock, loop, self.config) qos_mgr._redis_store = {} def populate_db(qid_list, old_ambr_list, old_leaf_list, rule_list): qos_mgr._redis_store.clear() for i, t in enumerate(rule_list): k = get_key_json(get_subscriber_key(*t)) v = get_data_json( get_subscriber_data( qid_list[i], old_ambr_list[i], old_leaf_list[i], ), ) qos_mgr._redis_store[k] = v MockSt = namedtuple("MockSt", "meter_id") dummy_meter_ev_body = [MockSt(11), MockSt(13), MockSt(2), MockSt(15)] def tc_read(intf): if intf == self.ul_intf: return [ (30, 3000), (15, 1500), (300, 3000), (150, 1500), (3000, 65534), (1500, 65534), ] else: return [ (13, 1300), (11, 1100), (130, 1300), (110, 1100), (1300, 65534), (1100, 65534), ] # prepopulate qos_store old_qid_list = [2, 11, 13, 30] old_leaf_list = [20, 110, 130, 300] old_ambr_list = [200, 1100, 1300, 3000] old_rule_list = [ ("1", '1.1.1.1', 0, 0), ("1", '1.1.1.2', 1, 0), ("1", '1.1.1.3', 2, 1), ("2", '1.1.1.4', 0, 0), ] populate_db(old_qid_list, old_ambr_list, old_leaf_list, old_rule_list) # mock future state if self.config["qos"]["impl"] == QosImplType.OVS_METER: qos_mgr.impl.handle_meter_config_stats(dummy_meter_ev_body) else: mock_traffic_cls.read_all_classes.side_effect = tc_read qos_mgr._initialized = False qos_mgr._setupInternal() # verify that qos_handle 20 not found in system is purged from map qid_list = [] for _, v in qos_mgr._redis_store.items(): _, qid, _, _ = get_data(v) qid_list.append(qid) logging.debug("qid_list %s", qid_list) self.assertNotIn(20, qid_list) # verify that unreferenced qos configs are purged from the system if self.config["qos"]["impl"] == QosImplType.OVS_METER: mock_meter_cls.del_meter.assert_called_with(MagicMock, 15) else: mock_traffic_cls.delete_class.assert_any_call(self.ul_intf, 15, False) mock_traffic_cls.delete_class.assert_any_call(self.ul_intf, 150, False) mock_traffic_cls.delete_class.assert_any_call(self.ul_intf, 1500, True) # add a new rule to the qos_mgr and check if it is assigned right id imsi, rule_num, d, qos_info = "3", 0, 0, QosInfo(100000, 100000) qos_mgr.impl.get_action_instruction = MagicMock qos_mgr.add_subscriber_qos(imsi, '', 10, rule_num, d, qos_info) k = get_key_json(get_subscriber_key(imsi, '', rule_num, d)) exp_id = 3 # since start_idx 2 is already used d3 = get_data_json(get_subscriber_data(exp_id + 1, exp_id - 1, exp_id)) self.assertEqual(qos_mgr._redis_store[k], d3) self.assertEqual(qos_mgr._subscriber_state[imsi].rules[rule_num][0], (d, d3)) # delete the restored rule - ensure that it gets cleaned properly purge_imsi = "1" purge_rule_num = 1 purge_qos_handle = 2 qos_mgr.remove_subscriber_qos(purge_imsi, purge_rule_num) self.assertTrue(purge_rule_num not in qos_mgr._subscriber_state[purge_imsi].rules) if self.config["qos"]["impl"] == QosImplType.OVS_METER: mock_meter_cls.del_meter.assert_called_with(MagicMock, purge_qos_handle) else: mock_traffic_cls.delete_class.assert_any_call(self.ul_intf, 11, False) mock_traffic_cls.delete_class.assert_any_call(self.ul_intf, 110, False) mock_traffic_cls.delete_class.assert_any_call(self.ul_intf, 1100, True) # case 2 - check with empty qos configs, qos_map gets purged mock_meter_cls.reset_mock() mock_traffic_cls.reset_mock() populate_db(old_qid_list, old_ambr_list, old_leaf_list, old_rule_list) # mock future state if self.config["qos"]["impl"] == QosImplType.OVS_METER: MockSt = namedtuple("MockSt", "meter_id") qos_mgr.impl._fut = loop.create_future() qos_mgr.impl.handle_meter_config_stats([]) else: mock_traffic_cls.read_all_classes.side_effect = lambda _: [] qos_mgr._initialized = False qos_mgr._setupInternal() self.assertTrue(not qos_mgr._redis_store) if self.config["qos"]["impl"] == QosImplType.OVS_METER: mock_meter_cls.del_meter.assert_not_called() else: mock_traffic_cls.delete_class.assert_not_called() # case 3 - check with empty qos_map, all qos configs get purged mock_meter_cls.reset_mock() mock_traffic_cls.reset_mock() qos_mgr._redis_store.clear() # mock future state if self.config["qos"]["impl"] == QosImplType.OVS_METER: qos_mgr.impl._fut = loop.create_future() qos_mgr.impl.handle_meter_config_stats(dummy_meter_ev_body) else: mock_traffic_cls.read_all_classes.side_effect = tc_read logging.debug("case three") qos_mgr._initialized = False qos_mgr._setupInternal() self.assertTrue(not qos_mgr._redis_store) # verify that unreferenced qos configs are purged from the system if self.config["qos"]["impl"] == QosImplType.OVS_METER: mock_meter_cls.del_meter.assert_any_call(MagicMock, 2) mock_meter_cls.del_meter.assert_any_call(MagicMock, 15) mock_meter_cls.del_meter.assert_any_call(MagicMock, 13) mock_meter_cls.del_meter.assert_any_call(MagicMock, 11) else: mock_traffic_cls.delete_class.assert_any_call(self.ul_intf, 15, False) mock_traffic_cls.delete_class.assert_any_call(self.ul_intf, 150, False) mock_traffic_cls.delete_class.assert_any_call(self.dl_intf, 13, False) mock_traffic_cls.delete_class.assert_any_call(self.dl_intf, 130, False) mock_traffic_cls.delete_class.assert_any_call(self.dl_intf, 11, False) mock_traffic_cls.delete_class.assert_any_call(self.dl_intf, 110, False)
def _testUncleanRestart(self, mock_meter_cls, mock_traffic_cls): """This test verifies the case when we recover the state upon restart. We verify the base case of reconciling differences between system qos configs and qos_store configs(real code uses redis hash, we simply use dict). Additionally we test cases when system qos configs were wiped out and qos store state was wiped out and ensure that eventually the system and qos store state remains consistent""" loop = asyncio.new_event_loop() qos_mgr = QosManager(loop, self.config, fakeredis.FakeStrictRedis()) qos_mgr.init_impl(MagicMock) qos_mgr._redis_store = {} def populate_db(qid_list, rule_list): qos_mgr._redis_store.clear() for i, t in enumerate(rule_list): k = get_key_json(get_subscriber_key(*t)) v = get_data_json(get_subscriber_data(qid_list[i], 0, 0)) qos_mgr._redis_store[k] = v MockSt = namedtuple("MockSt", "meter_id") dummy_meter_ev_body = [MockSt(11), MockSt(13), MockSt(2), MockSt(15)] def tc_read(intf): if intf == self.ul_intf: return [(2, 65534), (15, 65534)] else: return [(13, 65534), (11, 65534)] # prepopulate qos_store old_qid_list = [2, 11, 13, 20] old_rule_list = [ ("1", '1.1.1.1', 0, 0), ("1", '1.1.1.2', 1, 0), ("1", '1.1.1.3', 2, 1), ("2", '1.1.1.4', 0, 0), ] populate_db(old_qid_list, old_rule_list) # mock future state if self.config["qos"]["impl"] == QosImplType.OVS_METER: qos_mgr.impl.handle_meter_config_stats(dummy_meter_ev_body) else: mock_traffic_cls.read_all_classes.side_effect = tc_read qos_mgr._initialized = False qos_mgr._setupInternal() # verify that qos_handle 20 not found in system is purged from map qid_list = [] for _, v in qos_mgr._redis_store.items(): _, qid, _, _ = get_data(v) qid_list.append(qid) logging.debug("qid_list %s", qid_list) self.assertNotIn(20, qid_list) # verify that unreferenced qos configs are purged from the system if self.config["qos"]["impl"] == QosImplType.OVS_METER: mock_meter_cls.del_meter.assert_called_with(MagicMock, 15) else: mock_traffic_cls.delete_class.assert_called_with( self.ul_intf, 15, False) # add a new rule to the qos_mgr and check if it is assigned right id imsi, rule_num, d, qos_info = "3", 0, 0, QosInfo(100000, 100000) qos_mgr.impl.get_action_instruction = MagicMock qos_mgr.add_subscriber_qos(imsi, '', 0, rule_num, d, qos_info) k = get_key_json(get_subscriber_key(imsi, '', rule_num, d)) exp_id = 3 # since start_idx 2 is already used d3 = get_data_json(get_subscriber_data(exp_id, 0, 0)) self.assertEqual(qos_mgr._redis_store[k], d3) self.assertEqual(qos_mgr._subscriber_state[imsi].rules[rule_num][0], (d, d3)) # delete the restored rule - ensure that it gets cleaned properly purge_imsi = "1" purge_rule_num = 0 purge_qos_handle = 2 qos_mgr.remove_subscriber_qos(purge_imsi, purge_rule_num) self.assertTrue( purge_rule_num not in qos_mgr._subscriber_state[purge_imsi].rules) if self.config["qos"]["impl"] == QosImplType.OVS_METER: mock_meter_cls.del_meter.assert_called_with( MagicMock, purge_qos_handle) else: mock_traffic_cls.delete_class.assert_called_with( self.ul_intf, purge_qos_handle, False, ) # case 2 - check with empty qos configs, qos_map gets purged mock_meter_cls.reset_mock() mock_traffic_cls.reset_mock() populate_db(old_qid_list, old_rule_list) # mock future state if self.config["qos"]["impl"] == QosImplType.OVS_METER: MockSt = namedtuple("MockSt", "meter_id") qos_mgr.impl._fut = loop.create_future() qos_mgr.impl.handle_meter_config_stats([]) else: mock_traffic_cls.read_all_classes.side_effect = lambda _: [] qos_mgr._initialized = False qos_mgr._setupInternal() self.assertTrue(not qos_mgr._redis_store) if self.config["qos"]["impl"] == QosImplType.OVS_METER: mock_meter_cls.del_meter.assert_not_called() else: mock_traffic_cls.delete_class.assert_not_called() # case 3 - check with empty qos_map, all qos configs get purged mock_meter_cls.reset_mock() mock_traffic_cls.reset_mock() qos_mgr._redis_store.clear() # mock future state if self.config["qos"]["impl"] == QosImplType.OVS_METER: qos_mgr.impl._fut = loop.create_future() qos_mgr.impl.handle_meter_config_stats(dummy_meter_ev_body) else: mock_traffic_cls.read_all_classes.side_effect = tc_read qos_mgr._initialized = False qos_mgr._setupInternal() self.assertTrue(not qos_mgr._redis_store) # verify that unreferenced qos configs are purged from the system if self.config["qos"]["impl"] == QosImplType.OVS_METER: mock_meter_cls.del_meter.assert_any_call(MagicMock, 2) mock_meter_cls.del_meter.assert_any_call(MagicMock, 15) mock_meter_cls.del_meter.assert_any_call(MagicMock, 13) mock_meter_cls.del_meter.assert_any_call(MagicMock, 11) else: mock_traffic_cls.delete_class.assert_any_call( self.ul_intf, 2, False) mock_traffic_cls.delete_class.assert_any_call( self.ul_intf, 15, False) mock_traffic_cls.delete_class.assert_any_call( self.dl_intf, 13, False) mock_traffic_cls.delete_class.assert_any_call( self.dl_intf, 11, False)