def _testSanity( self, mock_meter_get_action_inst, mock_tc_get_action_inst, mock_meter_cls, mock_traffic_cls, ): """ This test verifies that qos configuration gets programmed correctly for addition and deletion of a single subscriber and a single rule, We additionally verify if the clean_restart wipes out everything """ # mock unclean state in qos prior_qids = {self.ul_intf: [(2, 0)], self.dl_intf: [(3, 0)]} if self.config["qos"]["impl"] == QosImplType.LINUX_TC: mock_traffic_cls.read_all_classes.side_effect = lambda intf: prior_qids[intf] qos_mgr = QosManager(MagicMock, asyncio.new_event_loop(), self.config) qos_mgr._qos_store = {} qos_mgr._setupInternal() imsi, ip_addr, rule_num, qos_info = "1234", '1.1.1.1', 0, QosInfo(100000, 100000) # add new subscriber qos queue qos_mgr.add_subscriber_qos(imsi, ip_addr, 0, rule_num, FlowMatch.UPLINK, qos_info) qos_mgr.add_subscriber_qos(imsi, ip_addr, 0, rule_num, FlowMatch.DOWNLINK, qos_info) k1 = get_json(get_subscriber_key(imsi, ip_addr, rule_num, FlowMatch.UPLINK)) k2 = get_json(get_subscriber_key(imsi, ip_addr, rule_num, FlowMatch.DOWNLINK)) ul_exp_id = qos_mgr.impl._start_idx dl_exp_id = qos_mgr.impl._start_idx + 1 self.assertTrue(qos_mgr._qos_store[k1] == ul_exp_id) self.assertTrue(qos_mgr._qos_store[k2] == dl_exp_id) self.assertTrue(qos_mgr._subscriber_state[imsi].rules[rule_num][0] == (0, ul_exp_id)) self.assertTrue(qos_mgr._subscriber_state[imsi].rules[rule_num][1] == (1, dl_exp_id)) # add the same subscriber and ensure that we didn't create another # qos config for the subscriber qos_mgr.add_subscriber_qos(imsi, ip_addr, 0, rule_num, FlowMatch.UPLINK, qos_info) qos_mgr.add_subscriber_qos(imsi, ip_addr, 0, rule_num, FlowMatch.DOWNLINK, qos_info) self.assertTrue(qos_mgr._subscriber_state[imsi].rules[rule_num][0] == (0, ul_exp_id)) self.assertTrue(qos_mgr._subscriber_state[imsi].rules[rule_num][1] == (1, dl_exp_id)) # verify if traffic class was invoked properly if self.config["qos"]["impl"] == QosImplType.OVS_METER: self.verifyMeterAddQos( mock_meter_get_action_inst, mock_meter_cls, FlowMatch.UPLINK, ul_exp_id, qos_info, ) self.verifyMeterAddQos( mock_meter_get_action_inst, mock_meter_cls, FlowMatch.DOWNLINK, dl_exp_id, qos_info, ) self.verifyMeterCleanRestart(mock_meter_cls) else: self.verifyTcAddQos( mock_tc_get_action_inst, mock_traffic_cls, FlowMatch.UPLINK, ul_exp_id, qos_info, ) self.verifyTcAddQos( mock_tc_get_action_inst, mock_traffic_cls, FlowMatch.DOWNLINK, dl_exp_id, qos_info, ) self.verifyTcCleanRestart(prior_qids, mock_traffic_cls) # remove the subscriber qos and verify things are cleaned up qos_mgr.remove_subscriber_qos(imsi, rule_num) self.assertTrue(len(qos_mgr._qos_store) == 0) self.assertTrue(imsi not in qos_mgr._subscriber_state) if self.config["qos"]["impl"] == QosImplType.OVS_METER: self.verifyMeterRemoveQos(mock_meter_cls, FlowMatch.UPLINK, ul_exp_id) self.verifyMeterRemoveQos(mock_meter_cls, FlowMatch.DOWNLINK, dl_exp_id) else: self.verifyTcRemoveQos(mock_traffic_cls, FlowMatch.UPLINK, ul_exp_id, show_error=False) self.verifyTcRemoveQos(mock_traffic_cls, FlowMatch.DOWNLINK, dl_exp_id, show_error=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(MagicMock, loop, self.config) qos_mgr._qos_store = {} def populate_db(qid_list, rule_list): qos_mgr._qos_store.clear() for i, t in enumerate(rule_list): k = get_json(get_subscriber_key(*t)) qos_mgr._qos_store[k] = qid_list[i] 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, 15] else: return [13, 11] # prepopulate qos_store old_qid_list = [2, 11, 13, 20] old_rule_list = [("1", 0, 0), ("1", 1, 0), ("1", 2, 1), ("2", 0, 0)] populate_db(old_qid_list, old_rule_list) # mock future state if self.config["qos"]["impl"] == QosImplType.OVS_METER: qos_mgr.qos_impl.handle_meter_config_stats(dummy_meter_ev_body) else: mock_traffic_cls.read_all_classes.side_effect = tc_read qos_mgr._setupInternal() # run async loop once to ensure ready items are cleared loop._run_once() # verify that qos_handle 20 not found in system is purged from map self.assertFalse([v for _, v in qos_mgr._qos_store.items() if v == 20]) # 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) # 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.qos_impl.get_action_instruction = MagicMock qos_mgr.add_subscriber_qos(imsi, rule_num, d, qos_info) k = get_json(get_subscriber_key(imsi, rule_num, d)) exp_id = 3 # since start_idx 2 is already used self.assertTrue(qos_mgr._qos_store[k] == exp_id) self.assertTrue(qos_mgr._subscriber_map[imsi][rule_num][0] == (exp_id, d)) # 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.qos_impl._fut = loop.create_future() qos_mgr.qos_impl.handle_meter_config_stats([]) else: mock_traffic_cls.read_all_classes.side_effect = lambda _: [] qos_mgr._setupInternal() # run async loop once to ensure ready items are cleared loop._run_once() self.assertTrue(not qos_mgr._qos_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._qos_store.clear() # mock future state if self.config["qos"]["impl"] == QosImplType.OVS_METER: qos_mgr.qos_impl._fut = loop.create_future() qos_mgr.qos_impl.handle_meter_config_stats(dummy_meter_ev_body) else: mock_traffic_cls.read_all_classes.side_effect = tc_read qos_mgr._setupInternal() # run async loop once to ensure ready items are cleared loop._run_once() self.assertTrue(not qos_mgr._qos_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) mock_traffic_cls.delete_class.assert_any_call(self.ul_intf, 15) mock_traffic_cls.delete_class.assert_any_call(self.dl_intf, 13) mock_traffic_cls.delete_class.assert_any_call(self.dl_intf, 11)
def add_subscriber_qos( self, imsi: str, ip_addr: str, apn_ambr: int, rule_num: int, direction: FlowMatch.Direction, qos_info: QosInfo, cleanup_rule=None, ): with self._lock: if not self._qos_enabled or not self._initialized: LOG.debug("add_subscriber_qos: not enabled or initialized") return None, None, None LOG.debug( "adding qos for imsi %s rule_num %d direction %d apn_ambr %d, %s", imsi, rule_num, direction, apn_ambr, qos_info, ) imsi = normalize_imsi(imsi) # ip_addr identifies a specific subscriber session, each subscriber session # must be associated with a default bearer and can be associated with dedicated # bearers. APN AMBR specifies the aggregate max bit rate for a specific # subscriber across all the bearers. Queues for dedicated bearers will be # children of default bearer Queues. In case the dedicated bearers exceed the # rate, then they borrow from the default bearer queue subscriber_state = self._get_or_create_subscriber(imsi) qos_handle = subscriber_state.get_qos_handle(rule_num, direction) if qos_handle: LOG.debug( "qos exists for imsi %s rule_num %d direction %d", imsi, rule_num, direction, ) return self.impl.get_action_instruction(qos_handle) ambr_qos_handle_root = 0 ambr_qos_handle_leaf = 0 if self._apn_ambr_enabled and apn_ambr > 0: session = subscriber_state.get_or_create_session(ip_addr) ambr_qos_handle_root = session.get_ambr(direction) LOG.debug("existing root rec: ambr_qos_handle_root %d", ambr_qos_handle_root) if not ambr_qos_handle_root: ambr_qos_handle_root = self.impl.add_qos( direction, QosInfo(gbr=None, mbr=apn_ambr), cleanup_rule, skip_filter=True, ) if not ambr_qos_handle_root: LOG.error( 'Failed adding root ambr qos mbr %u direction %d', apn_ambr, direction, ) return None, None, None else: LOG.debug( 'Added root ambr qos mbr %u direction %d qos_handle %d ', apn_ambr, direction, ambr_qos_handle_root, ) ambr_qos_handle_leaf = session.get_ambr_leaf(direction) LOG.debug("existing leaf rec: ambr_qos_handle_leaf %d", ambr_qos_handle_leaf) if not ambr_qos_handle_leaf: ambr_qos_handle_leaf = self.impl.add_qos( direction, QosInfo(gbr=None, mbr=apn_ambr), cleanup_rule, parent=ambr_qos_handle_root, ) if ambr_qos_handle_leaf: session.set_ambr(direction, ambr_qos_handle_root, ambr_qos_handle_leaf) LOG.debug( 'Added ambr qos mbr %u direction %d qos_handle %d/%d ', apn_ambr, direction, ambr_qos_handle_root, ambr_qos_handle_leaf, ) else: LOG.error( 'Failed adding leaf ambr qos mbr %u direction %d', apn_ambr, direction, ) self.impl.remove_qos(ambr_qos_handle_root, direction, skip_filter=True) return None, None, None qos_handle = ambr_qos_handle_leaf if qos_info: qos_handle = self.impl.add_qos( direction, qos_info, cleanup_rule, parent=ambr_qos_handle_root, ) LOG.debug("Added ded brr handle: %d", qos_handle) if qos_handle: LOG.debug( 'Adding qos %s direction %d qos_handle %d ', qos_info, direction, qos_handle, ) else: LOG.error('Failed adding qos %s direction %d', qos_info, direction) return None, None, None if qos_handle: subscriber_state.update_rule( ip_addr, rule_num, direction, qos_handle, ambr_qos_handle_root, ambr_qos_handle_leaf, ) return self.impl.get_action_instruction(qos_handle) return None, None, None
def _testMultipleSubscribers( self, mock_meter_get_action_inst, mock_tc_get_action_inst, mock_meter_cls, mock_traffic_cls, ): """ This test verifies that qos configuration gets programmed correctly for addition and deletion of a multiple subscribers and rules. we additionally run through different scenarios involving - deactivating a rule - deactivating a subscriber - creating gaps in deletion and verifying that new qos configs get programmed properly with appropriate qids - additionally we also verify the idempotency of deletion calls and ensure that code doesn't behave incorrectly when same items are deleted multiple times - Finally we delete everything and verify if that behavior is right""" qos_mgr = QosManager(MagicMock, asyncio.new_event_loop(), self.config) qos_mgr._qos_store = {} qos_mgr._setupInternal() rule_list1 = [("1", 0, 0), ("1", 1, 0), ("1", 2, 1), ("2", 0, 0)] rule_list2 = [ ("2", 1, 0), ("3", 0, 0), ("4", 0, 0), ("5", 0, 0), ("5", 1, 1), ("6", 0, 0), ] start_idx, end_idx = 2, 2 + len(rule_list1) id_list = list(range(start_idx, end_idx)) qos_info = QosInfo(100000, 100000) exp_id_dict = {} # add new subscriber qos queues for i, (imsi, rule_num, d) in enumerate(rule_list1): qos_mgr.add_subscriber_qos(imsi, rule_num, d, qos_info) exp_id = id_list[i] k = get_json(get_subscriber_key(imsi, rule_num, d)) exp_id_dict[k] = exp_id self.assertTrue(qos_mgr._qos_store[k] == exp_id) self.assertTrue( qos_mgr._subscriber_map[imsi][rule_num][0] == (exp_id, d)) if self.config["qos"]["impl"] == QosImplType.OVS_METER: self.verifyMeterAddQos(mock_meter_get_action_inst, mock_meter_cls, d, exp_id, qos_info) else: self.verifyTcAddQos(mock_tc_get_action_inst, mock_traffic_cls, d, exp_id, qos_info) # deactivate one rule # verify for imsi1 if rule num 0 gets cleaned up imsi, rule_num, d = rule_list1[0] k = get_json(get_subscriber_key(imsi, rule_num, d)) exp_id = exp_id_dict[k] qos_mgr.remove_subscriber_qos(imsi, rule_num) self.assertTrue(k not in qos_mgr._qos_store) self.assertTrue(rule_num not in qos_mgr._subscriber_map[imsi]) if self.config["qos"]["impl"] == QosImplType.OVS_METER: self.verifyMeterRemoveQos(mock_meter_cls, d, exp_id) else: self.verifyTcRemoveQos(mock_traffic_cls, d, exp_id) # deactivate same rule and check if we log properly with self.assertLogs("pipelined.qos.common", level="ERROR") as cm: qos_mgr.remove_subscriber_qos(imsi, rule_num) error_msg = "unable to find rule_num 0 for imsi 1" self.assertTrue(cm.output[0].endswith(error_msg)) # deactivate imsi # verify for imsi1 if rule num 1 and 2 gets cleaned up qos_mgr.remove_subscriber_qos(imsi) remove_qos_args = [] for imsi, rule_num, d in rule_list1[1:]: if imsi != "1": continue k = get_json(get_subscriber_key(imsi, rule_num, d)) exp_id = exp_id_dict[k] self.assertTrue(k not in qos_mgr._qos_store) remove_qos_args.append((d, exp_id)) if self.config["qos"]["impl"] == QosImplType.OVS_METER: self.verifyMeterRemoveQosBulk(mock_meter_cls, remove_qos_args) else: self.verifyTcRemoveQosBulk(mock_traffic_cls, remove_qos_args) self.assertTrue("1" not in qos_mgr._subscriber_map) # deactivate same imsi again and ensure nothing bad happens with self.assertLogs("pipelined.qos.common", level="DEBUG") as cm: qos_mgr.remove_subscriber_qos("1") error_msg = "unable to find imsi 1" self.assertTrue(error_msg in cm.output[-1]) # now only imsi2 should remain self.assertTrue(len(qos_mgr._qos_store) == 1) existing_qid = list(qos_mgr._qos_store.values())[0] # add second rule list and delete and verify if things work qos_info = QosInfo(100000, 200000) # additional 1 is for accomodating the existing imsi2RuleList1Qid start_idx, end_idx = 2, 2 + len(rule_list2) + 1 id_list = [i for i in range(start_idx, end_idx) if i != existing_qid] # add new subscriber qos queues for i, (imsi, rule_num, d) in enumerate(rule_list2): qos_mgr.add_subscriber_qos(imsi, rule_num, d, qos_info) exp_id = id_list[i] k = get_json(get_subscriber_key(imsi, rule_num, d)) self.assertTrue(qos_mgr._qos_store[k] == exp_id) self.assertTrue( qos_mgr._subscriber_map[imsi][rule_num][0] == (exp_id, d)) if self.config["qos"]["impl"] == QosImplType.OVS_METER: self.verifyMeterAddQos(mock_meter_get_action_inst, mock_meter_cls, d, exp_id, qos_info) else: self.verifyTcAddQos(mock_tc_get_action_inst, mock_traffic_cls, d, exp_id, qos_info) # delete the subscriber qos queues for i, (imsi, rule_num, d) in enumerate(rule_list2): qos_mgr.remove_subscriber_qos(imsi, rule_num) k = get_json(get_subscriber_key(imsi, rule_num, d)) self.assertTrue(k not in qos_mgr._qos_store) self.assertTrue(rule_num not in qos_mgr._subscriber_map[imsi]) if self.config["qos"]["impl"] == QosImplType.OVS_METER: self.verifyMeterRemoveQos(mock_meter_cls, d, id_list[i]) else: self.verifyTcRemoveQos(mock_traffic_cls, d, id_list[i]) # delete everything qos_mgr.remove_subscriber_qos() # imsi2 from rule_list1 alone wasn't removed imsi, rule_num, d = rule_list1[3] k = get_json(get_subscriber_key(imsi, rule_num, d)) self.assertTrue(not qos_mgr._qos_store) self.assertTrue(not qos_mgr._subscriber_map) if self.config["qos"]["impl"] == QosImplType.OVS_METER: self.verifyMeterRemoveQos(mock_meter_cls, d, existing_qid) else: self.verifyTcRemoveQos(mock_traffic_cls, d, existing_qid)
def testApnAmbrSanity( self, mock_tc_get_action_inst, mock_traffic_cls, ): """ This test verifies that qos configuration gets programmed correctly for addition and deletion of a single subscriber and a single rule, We additionally verify if the clean_restart wipes out everything """ self.config["qos"]["impl"] = QosImplType.LINUX_TC # mock unclean state in qos prior_qids = {self.ul_intf: [(2, 0)], self.dl_intf: [(3, 0)]} mock_traffic_cls.read_all_classes.side_effect = lambda intf: prior_qids[intf] qos_mgr = QosManager(MagicMock, asyncio.new_event_loop(), self.config) qos_mgr._redis_store = {} qos_mgr._setupInternal() ambr_ul, ambr_dl = 250000, 500000 imsi, ip_addr, rule_num, qos_info = ("1234", '1.1.1.1', 1, QosInfo(50000, 100000)) # add new subscriber qos queue qos_mgr.add_subscriber_qos(imsi, ip_addr, ambr_ul, rule_num, FlowMatch.UPLINK, qos_info) qos_mgr.add_subscriber_qos(imsi, ip_addr, ambr_dl, rule_num, FlowMatch.DOWNLINK, qos_info) k1 = get_json(get_subscriber_key(imsi, ip_addr, rule_num, FlowMatch.UPLINK)) k2 = get_json(get_subscriber_key(imsi, ip_addr, rule_num, FlowMatch.DOWNLINK)) ambr_ul_exp_id = qos_mgr.impl._start_idx ul_exp_id = qos_mgr.impl._start_idx + 2 ambr_dl_exp_id = qos_mgr.impl._start_idx + 3 dl_exp_id = qos_mgr.impl._start_idx + 5 self.assertEqual(qos_mgr._redis_store[k1], ul_exp_id) self.assertEqual(qos_mgr._redis_store[k2], dl_exp_id) self.assertTrue(qos_mgr._subscriber_state[imsi].rules[rule_num][0] == (0, ul_exp_id)) self.assertTrue(qos_mgr._subscriber_state[imsi].rules[rule_num][1] == (1, dl_exp_id)) self.assertEqual(len(qos_mgr._subscriber_state[imsi].sessions), 1) self.assertEqual(qos_mgr._subscriber_state[imsi].sessions[ip_addr].ambr_dl, ambr_dl_exp_id) self.assertEqual(qos_mgr._subscriber_state[imsi].sessions[ip_addr].ambr_dl_leaf, ambr_dl_exp_id + 1) self.assertEqual(qos_mgr._subscriber_state[imsi].sessions[ip_addr].ambr_ul_leaf, ambr_ul_exp_id + 1) # add the same subscriber and ensure that we didn't create another # qos config for the subscriber qos_mgr.add_subscriber_qos(imsi, ip_addr, 0, rule_num, FlowMatch.UPLINK, qos_info) qos_mgr.add_subscriber_qos(imsi, ip_addr, 0, rule_num, FlowMatch.DOWNLINK, qos_info) self.assertTrue(qos_mgr._subscriber_state[imsi].rules[rule_num][0] == (0, ul_exp_id)) self.assertTrue(qos_mgr._subscriber_state[imsi].rules[rule_num][1] == (1, dl_exp_id)) # verify if traffic class was invoked properly self.verifyTcAddQos(mock_tc_get_action_inst, mock_traffic_cls, FlowMatch.UPLINK, ul_exp_id, qos_info, parent_qid=ambr_ul_exp_id) self.verifyTcAddQos(mock_tc_get_action_inst,mock_traffic_cls, FlowMatch.DOWNLINK, dl_exp_id, qos_info, parent_qid=ambr_dl_exp_id) self.verifyTcCleanRestart(prior_qids, mock_traffic_cls) # remove the subscriber qos and verify things are cleaned up qos_mgr.remove_subscriber_qos(imsi, rule_num) self.assertTrue(len(qos_mgr._redis_store) == 0) self.assertTrue(imsi not in qos_mgr._subscriber_state) self.verifyTcRemoveQos(mock_traffic_cls, FlowMatch.UPLINK, ambr_ul_exp_id) self.verifyTcRemoveQos(mock_traffic_cls, FlowMatch.UPLINK, ul_exp_id) self.verifyTcRemoveQos(mock_traffic_cls, FlowMatch.DOWNLINK, ambr_dl_exp_id) self.verifyTcRemoveQos(mock_traffic_cls, FlowMatch.DOWNLINK, dl_exp_id)
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 add_subscriber_qos( self, imsi: str, ip_addr: str, apn_ambr: int, rule_num: int, direction: FlowMatch.Direction, qos_info: QosInfo, ): if not self._qos_enabled or not self._initialized: LOG.error("add_subscriber_qos: not enabled or initialized") return (None, None) LOG.debug("adding qos for imsi %s rule_num %d direction %d", imsi, rule_num, direction) imsi = normalize_imsi(imsi) # ip_addr identifies a specific subscriber session, each subscriber session # must be associated with a default bearer and can be associated with dedicated # bearers. APN AMBR specifies the aggregate max bit rate for a specific # subscriber across all the bearers. Queues for dedicated bearers will be # children of default bearer Queues. In case the dedicated bearers exceed the # rate, then they borrow from the default bearer queue subscriber_state = self.get_or_create_subscriber(imsi) qos_handle = subscriber_state.get_qos_handle(rule_num, direction) if qos_handle: LOG.debug("qos exists for imsi %s rule_num %d direction %d", imsi, rule_num, direction) return self.impl.get_action_instruction(qos_handle) if apn_ambr > 0: session = subscriber_state.get_or_create_session(ip_addr) ambr_qos_handle = session.get_ambr(direction) if not ambr_qos_handle: ambr_qos_handle = self.impl.add_qos( direction, QosInfo(gbr=0, mbr=apn_ambr)) if ambr_qos_handle: session.set_ambr(direction, ambr_qos_handle) LOG.debug( 'Adding ambr qos mbr %u direction %d qos_handle %d ', apn_ambr, direction, ambr_qos_handle) else: LOG.error('Failed adding ambr qos mbr %u direction %d', apn_ambr, direction) return if qos_info: qos_handle = self.impl.add_qos(direction, qos_info, parent=ambr_qos_handle) if qos_handle: LOG.debug('Adding qos %s direction %d qos_handle %d ', qos_info, direction, qos_handle) else: LOG.error('Failed adding qos %s direction %d', qos_info, direction) return else: qos_handle = ambr_qos_handle else: qos_handle = self.impl.add_qos(direction, qos_info) subscriber_state.update_rule(ip_addr, rule_num, direction, qos_handle) return self.impl.get_action_instruction(qos_handle)
def _get_action_for_rule( self, flow, rule_num, imsi, ip_addr, apn_ambr, qos, rule_id, version, qos_mgr, local_f_teid_ng, ): """ Returns an action instructions list to be applied for a specific flow. If qos or apn_ambr are set, the appropriate action is returned based on the implementation used (tc vs ovs_meter) """ parser = self._datapath.ofproto_parser instructions = [] qos_handle = 0 # encode the rule id in hex of_note = parser.NXActionNote(list(rule_id.encode())) actions = [of_note] if flow.action == flow.DENY: return actions, instructions, qos_handle mbr_ul = qos.max_req_bw_ul mbr_dl = qos.max_req_bw_dl qos_info = None ambr = None d = flow.match.direction if d == flow.match.UPLINK: if apn_ambr: ambr = apn_ambr.max_bandwidth_ul if mbr_ul != 0: qos_info = QosInfo(gbr=qos.gbr_ul, mbr=mbr_ul) if d == flow.match.DOWNLINK: if apn_ambr: ambr = apn_ambr.max_bandwidth_dl if mbr_dl != 0: qos_info = QosInfo(gbr=qos.gbr_dl, mbr=mbr_dl) if qos_info or ambr: cleanup_rule_func = lambda: self._invalidate_rule_version( imsi, ip_addr, rule_id, ) action, inst, qos_handle = qos_mgr.add_subscriber_qos( imsi, ip_addr.address.decode('utf8'), ambr, rule_num, d, qos_info, cleanup_rule_func, ) self.logger.debug("adding Actions %s instruction %s ", action, inst) if action: actions.append(action) if inst: instructions.append(inst) actions.extend([ parser.NXActionRegLoad2(dst=RULE_NUM_REG, value=rule_num), parser.NXActionRegLoad2(dst=RULE_VERSION_REG, value=version), parser.NXActionRegLoad2(dst=NG_SESSION_ID_REG, value=local_f_teid_ng), ], ) return actions, instructions, qos_handle