def read_msg(self, message: Any) -> AcsReadMsgResult: """ Process GetParameterValuesResponse Object parameters that have a reported value of None indicate that the object is not in the eNB's configuration. Most eNB devices will reply with a Fault message if we try to get values of parameters that don't exist on the data model, so this is an idiosyncrasy of Baicells QAFB. """ if not isinstance(message, models.GetParameterValuesResponse): return AcsReadMsgResult(False, None) path_to_val = {} for param_value_struct in message.ParameterList.ParameterValueStruct: path_to_val[param_value_struct.Name] = \ param_value_struct.Value.Data logger.debug('Received object parameters: %s', str(path_to_val)) num_plmns = self.acs.data_model.get_num_plmns() for i in range(1, num_plmns + 1): obj_name = ParameterName.PLMN_N % i obj_to_params = self.acs.data_model.get_numbered_param_names() param_name_list = obj_to_params[obj_name] for name in param_name_list: path = self.acs.data_model.get_parameter(name).path if path in path_to_val: value = path_to_val[path] if value is None: continue if obj_name not in self.acs.device_cfg.get_object_names(): self.acs.device_cfg.add_object(obj_name) magma_value = \ self.acs.data_model.transform_for_magma(name, value) self.acs.device_cfg.set_parameter_for_object(name, magma_value, obj_name) # Now we have enough information to build the desired configuration if self.acs.desired_cfg is None: self.acs.desired_cfg = build_desired_config( self.acs.mconfig, self.acs.service_config, self.acs.device_cfg, self.acs.data_model, self.acs.config_postprocessor, ) if len(get_all_objects_to_delete(self.acs.desired_cfg, self.acs.device_cfg)) > 0: return AcsReadMsgResult(True, self.rm_obj_transition) elif len(get_all_objects_to_add(self.acs.desired_cfg, self.acs.device_cfg)) > 0: return AcsReadMsgResult(True, self.add_obj_transition) elif len(get_all_param_values_to_set(self.acs.desired_cfg, self.acs.device_cfg, self.acs.data_model)) > 0: return AcsReadMsgResult(True, self.set_params_transition) return AcsReadMsgResult(True, self.skip_transition)
def test_frequency_related_params_removed_in_postprocessor(self): acs_state_machine = provision_clean_sm() acs_state_machine.device_cfg.set_parameter(ParameterName.IP_SEC_ENABLE, 'false') parameters_to_delete = [ ParameterName.RADIO_ENABLE, ParameterName.POWER_SPECTRAL_DENSITY, ParameterName.EARFCNDL, ParameterName.EARFCNUL, ParameterName.BAND, ParameterName.DL_BANDWIDTH, ParameterName.UL_BANDWIDTH, ParameterName.SAS_RADIO_ENABLE, ] for p in parameters_to_delete: acs_state_machine.device_cfg.set_parameter(p, 'some_value') desired_cfg = build_desired_config( acs_state_machine.mconfig, acs_state_machine.service_config, acs_state_machine.device_cfg, acs_state_machine.data_model, acs_state_machine.config_postprocessor, ) for p in parameters_to_delete: self.assertFalse(desired_cfg.has_parameter(p))
def _prepare_desired_cfg_for_sm(sm): return build_desired_config( sm.mconfig, sm.service_config, sm.device_cfg, sm.data_model, sm.config_postprocessor, )
def test_transition_depending_on_sas_enabled_flag( self, dp_mode, expected_state, mock_get_state, ): """Testing if SM steps in and out of FreedomFiOneWaitNotifyDPState as per state map depending on whether sas_enabled param is set to True or False in the service config Args: dp_mode: bool flag to enable or disable dp mode expected_state (Any): State mock_get_state (Any): mocking get_cbsd_state method """ mock_get_state.return_value = MOCK_CBSD_STATE acs_state_machine = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.FREEDOMFI_ONE) acs_state_machine._service.config = _get_service_config( dp_mode=dp_mode) acs_state_machine.desired_cfg = build_desired_config( acs_state_machine.mconfig, acs_state_machine.service_config, acs_state_machine.device_cfg, acs_state_machine.data_model, acs_state_machine.config_postprocessor, ) # Need to fill these values in the device_cfg if we're going to transition to notify_dp state acs_state_machine.device_cfg.set_parameter(SASParameters.SAS_USER_ID, 'test_user') acs_state_machine.device_cfg.set_parameter(SASParameters.SAS_FCC_ID, 'test_fcc') acs_state_machine.device_cfg.set_parameter(ParameterName.SERIAL_NUMBER, 'test_sn') acs_state_machine.transition('check_wait_get_params') msg = Tr069MessageBuilder.param_values_qrtb_response( [], models.GetParameterValuesResponse) # SM should transition from check_wait_get_params to end_session -> notify_dp automatically # upon receiving response from the radio acs_state_machine.handle_tr069_message(msg) self.assertIsInstance(acs_state_machine.state, expected_state) msg = Tr069MessageBuilder.get_inform(event_codes=['1 BOOT']) # SM should go into wait_inform state, respond with Inform response and transition to FreedomFiOneGetInitState acs_state_machine.handle_tr069_message(msg) self.assertIsInstance(acs_state_machine.state, FreedomFiOneGetInitState)
def read_msg(self, message: Any) -> AcsReadMsgResult: """ Process GetParameterValuesResponse """ if not isinstance(message, models.GetParameterValuesResponse): return AcsReadMsgResult(False, None) path_to_val = {} for param_value_struct in message.ParameterList.ParameterValueStruct: path_to_val[param_value_struct.Name] = \ param_value_struct.Value.Data logging.debug('Received object parameters: %s', str(path_to_val)) # TODO: This might a string for some strange reason, investigate why # Get the names of parameters belonging to numbered objects num_plmns = \ int(self.acs.device_cfg.get_parameter(ParameterName.NUM_PLMNS)) for i in range(1, num_plmns + 1): obj_name = ParameterName.PLMN_N % i obj_to_params = self.acs.data_model.get_numbered_param_names() param_name_list = obj_to_params[obj_name] for name in param_name_list: path = self.acs.data_model.get_parameter(name).path value = path_to_val[path] magma_val = \ self.acs.data_model.transform_for_magma(name, value) self.acs.device_cfg.set_parameter_for_object( name, magma_val, obj_name) # Now we can have the desired state if self.acs.desired_cfg is None: self.acs.desired_cfg = build_desired_config( self.acs.mconfig, self.acs.service_config, self.acs.device_cfg, self.acs.data_model, self.acs.config_postprocessor, ) if len( get_all_objects_to_delete(self.acs.desired_cfg, self.acs.device_cfg)) > 0: return AcsReadMsgResult(True, self.rm_obj_transition) elif len( get_all_objects_to_add(self.acs.desired_cfg, self.acs.device_cfg)) > 0: return AcsReadMsgResult(True, self.add_obj_transition) elif len( get_all_param_values_to_set(self.acs.desired_cfg, self.acs.device_cfg, self.acs.data_model)) > 0: return AcsReadMsgResult(True, self.set_params_transition) return AcsReadMsgResult(True, self.skip_transition)
def test_sas_enable_mode_is_enabled(self): acs_state_machine = provision_clean_sm() acs_state_machine.device_cfg.set_parameter(ParameterName.IP_SEC_ENABLE, 'false') acs_state_machine.desired_cfg = build_desired_config( acs_state_machine.mconfig, acs_state_machine.service_config, acs_state_machine.device_cfg, acs_state_machine.data_model, acs_state_machine.config_postprocessor, ) self.assertEqual( 1, acs_state_machine.desired_cfg.get_parameter( ParameterName.SAS_ENABLED))
def read_msg(self, message: Any) -> AcsReadMsgResult: """ Process GetParameterValuesResponse """ if not isinstance(message, models.GetParameterValuesResponse): return AcsReadMsgResult(False, None) path_to_val = {} if hasattr(message.ParameterList, 'ParameterValueStruct') and \ message.ParameterList.ParameterValueStruct is not None: for param_value_struct in message.ParameterList.ParameterValueStruct: path_to_val[param_value_struct.Name] = \ param_value_struct.Value.Data logger.debug('Received object parameters: %s', str(path_to_val)) # Number of PLMN objects reported can be incorrect. Let's count them num_plmns = 0 obj_to_params = self.acs.data_model.get_numbered_param_names() while True: obj_name = ParameterName.PLMN_N % (num_plmns + 1) if obj_name not in obj_to_params or len( obj_to_params[obj_name]) == 0: logger.warning( "eNB has PLMN %s but not defined in model", obj_name, ) break param_name_list = obj_to_params[obj_name] obj_path = self.acs.data_model.get_parameter( param_name_list[0]).path if obj_path not in path_to_val: break if not self.acs.device_cfg.has_object(obj_name): self.acs.device_cfg.add_object(obj_name) num_plmns += 1 for name in param_name_list: path = self.acs.data_model.get_parameter(name).path value = path_to_val[path] magma_val = \ self.acs.data_model.transform_for_magma(name, value) self.acs.device_cfg.set_parameter_for_object( name, magma_val, obj_name, ) num_plmns_reported = \ int(self.acs.device_cfg.get_parameter(ParameterName.NUM_PLMNS)) if num_plmns != num_plmns_reported: logger.warning( "eNB reported %d PLMNs but found %d", num_plmns_reported, num_plmns, ) self.acs.device_cfg.set_parameter( ParameterName.NUM_PLMNS, num_plmns, ) # Now we can have the desired state if self.acs.desired_cfg is None: self.acs.desired_cfg = build_desired_config( self.acs.mconfig, self.acs.service_config, self.acs.device_cfg, self.acs.data_model, self.acs.config_postprocessor, ) if len( get_all_objects_to_delete( self.acs.desired_cfg, self.acs.device_cfg, ), ) > 0: return AcsReadMsgResult(True, self.rm_obj_transition) elif len( get_all_objects_to_add( self.acs.desired_cfg, self.acs.device_cfg, ), ) > 0: return AcsReadMsgResult(True, self.add_obj_transition) elif len( get_all_param_values_to_set( self.acs.desired_cfg, self.acs.device_cfg, self.acs.data_model, ), ) > 0: return AcsReadMsgResult(True, self.set_params_transition) return AcsReadMsgResult(True, self.skip_transition)
def read_msg(self, message: Any) -> AcsReadMsgResult: """ Process GetParameterValuesResponse """ if not isinstance(message, models.GetParameterValuesResponse): return AcsReadMsgResult(msg_handled=False, next_state=None) path_to_val = {} for param_value_struct in message.ParameterList.ParameterValueStruct: path_to_val[param_value_struct.Name] = \ param_value_struct.Value.Data EnodebdLogger.debug('Received object parameters: %s', str(path_to_val)) # Parse simple params param_name_list = self.acs.data_model.get_parameter_names() for name in param_name_list: path = self.acs.data_model.get_parameter(name).path if path in path_to_val: value = path_to_val.get(path) magma_val = \ self.acs.data_model.transform_for_magma( name, value, ) self.acs.device_cfg.set_parameter(name, magma_val) # Parse object params num_plmns = self.acs.data_model.get_num_plmns() for i in range(1, num_plmns + 1): obj_name = ParameterName.PLMN_N % i obj_to_params = self.acs.data_model.get_numbered_param_names() param_name_list = obj_to_params[obj_name] for name in param_name_list: path = self.acs.data_model.get_parameter(name).path if path in path_to_val: value = path_to_val.get(path) if value is None: continue if obj_name not in self.acs.device_cfg.get_object_names(): self.acs.device_cfg.add_object(obj_name) magma_value = \ self.acs.data_model.transform_for_magma(name, value) self.acs.device_cfg.set_parameter_for_object( name, magma_value, obj_name, ) # Now we have enough information to build the desired configuration if self.acs.desired_cfg is None: self.acs.desired_cfg = build_desired_config( self.acs.mconfig, self.acs.service_config, self.acs.device_cfg, self.acs.data_model, self.acs.config_postprocessor, ) if len( get_all_objects_to_delete( self.acs.desired_cfg, self.acs.device_cfg, ), ) > 0: return AcsReadMsgResult( msg_handled=True, next_state=self.rm_obj_transition, ) elif len( get_all_objects_to_add( self.acs.desired_cfg, self.acs.device_cfg, ), ) > 0: return AcsReadMsgResult( msg_handled=True, next_state=self.add_obj_transition, ) elif len( get_all_param_values_to_set( self.acs.desired_cfg, self.acs.device_cfg, self.acs.data_model, ), ) > 0: return AcsReadMsgResult( msg_handled=True, next_state=self.set_params_transition, ) return AcsReadMsgResult( msg_handled=True, next_state=self.skip_transition, )
def test_provision(self, mock_get_state) -> None: """ Test the basic provisioning workflow 1 - enodeb sends Inform, enodebd sends InformResponse 2 - enodeb sends empty HTTP message, 3 - enodebd sends get transient params, updates the device state. 4 - enodebd sends get param values, updates the device state 5 - enodebd, sets fields including SAS fields. Args: mock_get_state (Any): mocking get_cbsd_state method """ mock_get_state.return_value = MOCK_CBSD_STATE logging.root.level = logging.DEBUG acs_state_machine = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.FREEDOMFI_ONE) acs_state_machine._service.config = _get_service_config() acs_state_machine.desired_cfg = build_desired_config( acs_state_machine.mconfig, acs_state_machine.service_config, acs_state_machine.device_cfg, acs_state_machine.data_model, acs_state_machine.config_postprocessor, ) inform = Tr069MessageBuilder.get_inform( oui="000E8F", sw_version="TEST3920@210901", enb_serial="2006CW5000023", ) resp = acs_state_machine.handle_tr069_message(inform) self.assertTrue( isinstance(resp, models.InformResponse), 'Should respond with an InformResponse', ) # Send an empty http request req = models.DummyInput() resp = acs_state_machine.handle_tr069_message(req) # Expect a request for read-only params self.assertTrue( isinstance(resp, models.GetParameterValues), 'State machine should be requesting param values', ) for tr69nodes in StatusParameters.STATUS_PARAMETERS.values(): self.assertIn(tr69nodes.path, resp.ParameterNames.string) req = self._get_ff_one_read_only_param_values_resp() get_resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(get_resp, models.GetParameterValues), 'State machine should be requesting param values', ) req = self._get_freedomfi_one_param_values_response() resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(resp, models.SetParameterValues), 'State machine should be setting parameters', ) self.assertIsNotNone( resp.ParameterKey.Data, 'ParameterKey should be set for FreedomFiOne eNB', ) msg = models.SetParameterValuesResponse() msg.Status = 1 get_resp = acs_state_machine.handle_tr069_message(msg) self.assertTrue( isinstance(get_resp, models.GetParameterValues), 'We should read back all parameters', ) req = self._get_freedomfi_one_param_values_response() resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(resp, models.DummyInput), 'Provisioning completed with Dummy response', )
def test_device_and_desired_config_discrepancy_after_initial_configuration( self): """ Testing a situation where device_cfg and desired_cfg are already present on the state machine, because the initial configuration of the radio has occurred, but then the configs have diverged (e.g. as a result of domain-proxy setting different values on the desired config) """ # Skipping previous states acs_state_machine = provision_clean_sm('wait_get_transient_params') # Need to set this param on the device_cfg first, otherwise we won't be able to generate the desired_cfg # using 'build_desired_config' function acs_state_machine.device_cfg.set_parameter(ParameterName.IP_SEC_ENABLE, 'false') acs_state_machine.desired_cfg = build_desired_config( acs_state_machine.mconfig, acs_state_machine.service_config, acs_state_machine.device_cfg, acs_state_machine.data_model, acs_state_machine.config_postprocessor, ) prepare_device_cfg_same_as_desired_cfg(acs_state_machine) # Let's say that while in 'wait_for_dp' state, the DP asked us to change this param's value in the desired_cfg acs_state_machine.desired_cfg.set_parameter(ParameterName.EARFCNDL, 5) req = Tr069MessageBuilder.param_values_qrtb_response( GET_TRANSIENT_PARAMS_RESPONSE_PARAMS, models.GetParameterValuesResponse, ) # ACS asking for all params from data model acs_state_machine.handle_tr069_message(req) req = Tr069MessageBuilder.param_values_qrtb_response( GET_PARAMS_RESPONSE_PARAMS, models.GetParameterValuesResponse, ) # ACS asking for object params acs_state_machine.handle_tr069_message(req) req = Tr069MessageBuilder.get_object_param_values_response( cell_reserved_for_operator_use='true', enable='true', is_primary='true', plmnid='00101', ) # ACS should ask the radio to correct the only parameter that doesn't match the desired config - EARFCNDL resp = acs_state_machine.handle_tr069_message(req) self.assertIsInstance(resp, models.SetParameterValues) self.assertEqual(1, len(resp.ParameterList.ParameterValueStruct)) self.assertEqual( 'Device.Services.FAPService.1.CellConfig.LTE.RAN.RF.EARFCNDL', resp.ParameterList.ParameterValueStruct[0].Name, ) self.assertEqual('5', resp.ParameterList.ParameterValueStruct[0].Value.Data)