def process_update( self, stream_name: str, updates: List[DataUpdate], resync: bool, ): logging.info('Processing %d SID -> apn -> policy updates', len(updates)) all_subscriber_rules = [] # type: List[RulesPerSubscriber] for update in updates: imsi = update.key subApnPolicies = SubscriberPolicySet() subApnPolicies.ParseFromString(update.value) is_updated = self._are_sub_policies_updated(imsi, subApnPolicies) if is_updated: all_subscriber_rules.append( self._build_sub_rule_set(imsi, subApnPolicies)) self._apn_rules_by_sid[imsi] = subApnPolicies logging.info('Updating %d IMSIs with new APN->policy assignments', len(all_subscriber_rules)) update = SessionRules(rules_per_subscriber=all_subscriber_rules) try: self._session_mgr_stub.SetSessionRules(update, timeout=5) except grpc.RpcError as e: logging.error('Unable to apply apn->policy updates %s', str(e))
def setUp(self): rating_groups_by_id = { 1: RatingGroup( id=1, limit_type=RatingGroup.INFINITE_UNMETERED, ), 2: RatingGroup( id=2, limit_type=RatingGroup.INFINITE_METERED, ), } basenames_dict = { 'bn1': ChargingRuleNameSet(RuleNames=['p5']), 'bn2': ChargingRuleNameSet(RuleNames=['p6']), } apn_rules_by_sid = { "IMSI1234": SubscriberPolicySet(rules_per_apn=[ ApnPolicySet( apn="apn1", assigned_base_names=[], assigned_policies=["redirect"], ), ], ), "IMSI2345": SubscriberPolicySet(rules_per_apn=[ ApnPolicySet( apn="apn1", assigned_base_names=["bn1"], assigned_policies=[], ), ApnPolicySet( apn="apn2", assigned_base_names=["bn2"], assigned_policies=[], ), ], ), "IMSI3456": SubscriberPolicySet( global_base_names=["bn1"], global_policies=[], rules_per_apn=[ ApnPolicySet( apn="apn1", assigned_base_names=[], assigned_policies=[], ), ], ), } self.servicer = SessionRpcServicer( self._get_mconfig(), rating_groups_by_id, basenames_dict, apn_rules_by_sid, )
def _handle_update( self, subscriber_id: str, assigned_policies: AssignedPolicies, ): """ Based on the streamed updates, find the delta in added and removed rules. Then make a RAR to send to sessiond. If all goes successfully, update Redis with the currently installed policies for the subscriber. """ prev_rules = self._get_prev_policies(subscriber_id) desired_rules = self._get_desired_rules(assigned_policies) self._apn_rules_by_sid[subscriber_id] = SubscriberPolicySet( rules_per_apn=[], global_policies=[ rule_id for rule_id in assigned_policies.assigned_policies ], global_base_names=[ basename for basename in assigned_policies.assigned_base_names ], ) rar = self._generate_rar(subscriber_id, list(desired_rules - prev_rules), list(prev_rules - desired_rules)) self._reauth_handler.handle_policy_re_auth(rar)
def process_update( self, stream_name: str, updates: List[DataUpdate], resync: bool, ): logging.info( 'Processing %d SID -> apn -> policy updates', len(updates), ) all_subscriber_rules = [] # type: List[RulesPerSubscriber] found_update = False for update in updates: imsi = update.key subApnPolicies = SubscriberPolicySet() subApnPolicies.ParseFromString(update.value) is_updated = self._are_sub_policies_updated(imsi, subApnPolicies) if is_updated: found_update = True all_subscriber_rules.append( self._build_sub_rule_set(imsi, subApnPolicies), ) self._apn_rules_by_sid[imsi] = subApnPolicies if not found_update: logging.debug( "No IMSIs with APN->Policy assignments found. " "Not sending an update to SessionD", ) return logging.info( 'Updating %d IMSIs with new APN->policy assignments', len(all_subscriber_rules), ) update = SessionRules(rules_per_subscriber=all_subscriber_rules) try: self._session_mgr_stub.SetSessionRules(update, timeout=5) except grpc.RpcError as e: error_extra = ( EXCLUDE_FROM_ERROR_MONITORING if indicates_connection_error(e) else None ) logging.error( "Unable to apply apn->policy updates %s", str(e), extra=error_extra, )
def _are_sub_policies_updated( self, subscriber_id: str, subApnPolicies: SubscriberPolicySet, ) -> bool: if subscriber_id not in self._apn_rules_by_sid: return True prev = self._apn_rules_by_sid[subscriber_id] # TODO: (8/21/2020) repeated fields may not be ordered the same, use a # different method to compare later return subApnPolicies.SerializeToString() != prev.SerializeToString()
def test_Update(self): """ Test the happy path where updates come in for rules, and sessiond accepts the SessionRules without issue. """ # Expected call arguments to SetSessionRules allow_all_flow_list = [ FlowDescription( match=FlowMatch( direction=FlowMatch.Direction.Value("UPLINK"), ), action=FlowDescription.Action.Value("PERMIT"), ), FlowDescription( match=FlowMatch( direction=FlowMatch.Direction.Value("DOWNLINK"), ), action=FlowDescription.Action.Value("PERMIT"), ), ] # type: List[FlowDescription] no_tracking_type = PolicyRule.TrackingType.Value("NO_TRACKING") expected = SessionRules(rules_per_subscriber=[ RulesPerSubscriber( imsi='imsi_1', rule_set=[ RuleSet( apply_subscriber_wide=False, apn="apn1", static_rules=[ StaticRuleInstall(rule_id="p1"), ], dynamic_rules=[ DynamicRuleInstall(policy_rule=PolicyRule( id="allowlist_sid-imsi_1-apn1", priority=2, flow_list=allow_all_flow_list, tracking_type=no_tracking_type, )) ], ), ]), RulesPerSubscriber( imsi='imsi_2', rule_set=[ RuleSet( apply_subscriber_wide=False, apn="apn1", static_rules=[ StaticRuleInstall(rule_id="p5"), ], dynamic_rules=[ DynamicRuleInstall(policy_rule=PolicyRule( id="allowlist_sid-imsi_2-apn1", priority=2, flow_list=allow_all_flow_list, tracking_type=no_tracking_type, )) ], ), ]) ]) # Setup the test apn_rules_dict = {} basenames_dict = { 'bn1': ChargingRuleNameSet(RuleNames=['p5']), 'bn2': ChargingRuleNameSet(RuleNames=['p6']), } stub = MockLocalSessionManagerStub() stub_call_args = [] # type: List[SessionRules] side_effect = get_SetSessionRules_side_effect(stub_call_args) stub.SetSessionRules = Mock(side_effect=side_effect) callback = ApnRuleMappingsStreamerCallback( stub, basenames_dict, apn_rules_dict, ) # Construct a set of updates, keyed by subscriber ID updates = [ DataUpdate( key="imsi_1", value=SubscriberPolicySet(rules_per_apn=[ ApnPolicySet( apn="apn1", assigned_base_names=[], assigned_policies=["p1"], ), ], ).SerializeToString(), ), DataUpdate( key="imsi_2", value=SubscriberPolicySet(rules_per_apn=[ ApnPolicySet( apn="apn1", assigned_base_names=["bn1"], assigned_policies=[], ), ], ).SerializeToString(), ), ] callback.process_update("stream", updates, False) # Since we used a stub which always succeeds when a RAR is made, # We should expect the assignments_dict to be updated imsi_1_policies = apn_rules_dict["imsi_1"] self.assertEqual(len(imsi_1_policies.rules_per_apn), 1, 'There should be 1 active APNs for imsi_1') self.assertEqual(len(stub_call_args), 1, 'Stub should have been called once') called_with = stub_call_args[0].SerializeToString() self.assertEqual(called_with, expected.SerializeToString(), 'SetSessionRules call has incorrect arguments') # Stream down a second update, and now IMSI_1 gets access to a new APN updates_2 = [ DataUpdate( key="imsi_1", value=SubscriberPolicySet(rules_per_apn=[ ApnPolicySet( apn="apn2", assigned_base_names=["bn1"], assigned_policies=[], ), ], ).SerializeToString(), ), DataUpdate( key="imsi_2", value=SubscriberPolicySet( global_base_names=["bn2"], global_policies=[], rules_per_apn=[ ApnPolicySet( apn="apn1", assigned_base_names=[], assigned_policies=[], ), ], ).SerializeToString(), ), ] expected_2 = SessionRules(rules_per_subscriber=[ RulesPerSubscriber( imsi='imsi_1', rule_set=[ RuleSet( apply_subscriber_wide=False, apn="apn2", static_rules=[ StaticRuleInstall(rule_id="p5"), ], dynamic_rules=[ DynamicRuleInstall(policy_rule=PolicyRule( id="allowlist_sid-imsi_1-apn2", priority=2, flow_list=allow_all_flow_list, tracking_type=no_tracking_type, )) ], ), ]), RulesPerSubscriber( imsi='imsi_2', rule_set=[ RuleSet( apply_subscriber_wide=False, apn="apn1", static_rules=[ StaticRuleInstall(rule_id="p6"), ], dynamic_rules=[ DynamicRuleInstall(policy_rule=PolicyRule( id="allowlist_sid-imsi_2-apn1", priority=2, flow_list=allow_all_flow_list, tracking_type=no_tracking_type, )) ], ), ]), ]) callback.process_update("stream", updates_2, False) imsi_1_policies = apn_rules_dict["imsi_1"] self.assertEqual(len(imsi_1_policies.rules_per_apn), 1, 'There should be 1 active APNs for imsi_1') self.assertEqual(len(stub_call_args), 2, 'Stub should have been called twice') called_with = stub_call_args[1].SerializeToString() self.assertEqual(called_with, expected_2.SerializeToString(), 'SetSessionRules call has incorrect arguments')