def test_autoremediation_from_fault(self): """ Transition the state machine into the unexpected fault state, then verify that it transitions itself back to WaitInform after an Inform is received. """ sm = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.BAICELLS, ) # Send an initial inform inform_msg = Tr069MessageBuilder.get_inform( '48BF74', 'BaiBS_RTS_3.1.6', '120200002618AGP0003', ['2 PERIODIC'], ) resp = sm.handle_tr069_message(inform_msg) self.assertTrue( isinstance(resp, models.InformResponse), 'Should respond with an InformResponse', ) # Now send a fault req = models.Fault() req.FaultCode = 12345 req.FaultString = 'Test FaultString' sm.handle_tr069_message(req) self.assertTrue('Error' in sm.get_state(), 'Should be in error state') # Send the Inform again, verify SM transitions out of fault resp = sm.handle_tr069_message(inform_msg) self.assertTrue(isinstance(resp, models.DummyInput)) self.assertEqual('Waiting for an Inform', sm.get_state())
def provision_clean_sm(state=None): acs_state_machine = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.BAICELLS_QRTB) acs_state_machine.desired_cfg = EnodebConfiguration( BaicellsQRTBTrDataModel(), ) if state is not None: acs_state_machine.transition(state) return acs_state_machine
def setUp(self): # Set up the ACS self.enb_acs_manager = EnodebAcsStateMachineBuilder.build_acs_manager() self.handler = EnodebAcsStateMachineBuilder.build_acs_state_machine() AutoConfigServer.set_state_machine_manager(self.enb_acs_manager) def side_effect(*args, **_kwargs): msg = args[1] return msg self.p = patch.object(AutoConfigServer, '_handle_tr069_message', Mock(side_effect=side_effect)) self.p.start() self.app = Tr069Application([AutoConfigServer], models.CWMP_NS, in_protocol=Tr069Soap11(validator='soft'), out_protocol=Tr069Soap11())
def test_manual_reboot_during_provisioning(self) -> None: """ Test a scenario where a Magma user goes through the enodebd CLI to reboot the Sercomm eNodeB. This checks the scenario where the command is sent in the middle of a TR-069 provisioning session. """ logging.root.level = logging.DEBUG acs_state_machine = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.FREEDOMFI_ONE) # Send an Inform message, wait for an InformResponse 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 to kick off the rest of provisioning req = models.DummyInput() resp = acs_state_machine.handle_tr069_message(req) # Expect a request for an optional parameter, three times self.assertTrue( isinstance(resp, models.GetParameterValues), 'State machine should be requesting param values', ) req = Tr069MessageBuilder.get_fault() # User uses the CLI tool to get eNodeB to reboot acs_state_machine.reboot_asap() resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(resp, models.Reboot), 'In reboot sequence, state machine should send a ' 'Reboot message.', ) req = Tr069MessageBuilder.get_reboot_response() resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(resp, models.DummyInput), 'State machine should end TR-069 session after ' 'receiving a RebootResponse', ) self.assertIsInstance(acs_state_machine.state, WaitInformMRebootState) inform = Tr069MessageBuilder.get_inform(event_codes=["M Reboot"], ) resp = acs_state_machine.handle_tr069_message(inform) self.assertIsInstance(resp, models.InformResponse) self.assertIsInstance(acs_state_machine.state, FreedomFiOneGetInitState)
def build_freedomfi_one_acs_state_machine(self): service = EnodebAcsStateMachineBuilder.build_magma_service( mconfig=EnodebConfigBuilder.get_mconfig(), service_config=self._get_service_config(), ) handler_class = get_device_handler_from_name( EnodebDeviceName.FREEDOMFI_ONE, ) acs_state_machine = handler_class(service) return acs_state_machine
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 test_manual_reboot_during_provisioning(self) -> None: """ Test a scenario where a Magma user goes through the enodebd CLI to reboot the Baicells eNodeB. This checks the scenario where the command is sent in the middle of a TR-069 provisioning session. """ acs_state_machine = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.BAICELLS_QRTB) # Send an Inform message, wait for an InformResponse inform_msg = Tr069MessageBuilder.get_qrtb_inform( params=DEFAULT_INFORM_PARAMS, oui='48BF74', enb_serial='1202000181186TB0006', event_codes=['2 PERIODIC'], ) resp = acs_state_machine.handle_tr069_message(inform_msg) self.assertTrue( isinstance(resp, models.InformResponse), 'Should respond with an InformResponse', ) # Send an empty http request to kick off the rest of provisioning req = models.DummyInput() resp = acs_state_machine.handle_tr069_message(req) # Expect a request for an optional parameter, three times self.assertTrue( isinstance(resp, models.GetParameterValues), 'State machine should be requesting param values', ) req = Tr069MessageBuilder.get_fault() # User uses the CLI tool to get eNodeB to reboot acs_state_machine.reboot_asap() resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(resp, models.Reboot), 'In reboot sequence, state machine should send a ' 'Reboot message.', ) req = Tr069MessageBuilder.get_reboot_response() resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(resp, models.DummyInput), 'State machine should end TR-069 session after ' 'receiving a RebootResponse', )
def test_notify_dp_sets_values_received_by_dp_in_desired_config( self, mock_get_state) -> None: expected_final_param_values = { ParameterName.UL_BANDWIDTH: '100', ParameterName.DL_BANDWIDTH: '100', ParameterName.EARFCNUL: 55340, ParameterName.EARFCNDL: 55340, ParameterName.POWER_SPECTRAL_DENSITY: 34, } test_user = '******' test_fcc_id = 'fcc_id' test_serial_number = '123' acs_state_machine = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.BAICELLS_QRTB) acs_state_machine.desired_cfg = EnodebConfiguration( BaicellsQRTBTrDataModel()) acs_state_machine.device_cfg.set_parameter(ParameterName.SAS_USER_ID, test_user) acs_state_machine.device_cfg.set_parameter(ParameterName.SAS_FCC_ID, test_fcc_id) acs_state_machine.device_cfg.set_parameter(ParameterName.SERIAL_NUMBER, test_serial_number) for param in expected_final_param_values: with self.assertRaises(KeyError): acs_state_machine.desired_cfg.get_parameter(param) # Skip previous steps not to duplicate the code acs_state_machine.transition('check_wait_get_params') req = Tr069MessageBuilder.param_values_qrtb_response( [], models.GetParameterValuesResponse, ) mock_get_state.return_value = MOCK_CBSD_STATE resp = acs_state_machine.handle_tr069_message(req) mock_get_state.assert_called_with( CBSDRequest(serial_number=test_serial_number), ) self.assertTrue(isinstance(resp, models.DummyInput)) for param, value in expected_final_param_values.items(): self.assertEqual( value, acs_state_machine.desired_cfg.get_parameter(param), )
def _check_postprocessing(self, expected, service_cfg): cfg_desired = Mock() acs_state_machine = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.FREEDOMFI_ONE) acs_state_machine.device_cfg.set_parameter( ParameterName.SERIAL_NUMBER, "2006CW5000023", ) cfg_init = FreedomFiOneConfigurationInitializer(acs_state_machine) cfg_init.postprocess( EnodebConfigBuilder.get_mconfig(), service_cfg, cfg_desired, ) cfg_desired.assert_has_calls(expected)
def test_manual_reboot(self) -> None: """ Test a scenario where a Magma user goes through the enodebd CLI to reboot the Baicells eNodeB. This checks the scenario where the command is not sent in the middle of a TR-069 provisioning session. """ acs_state_machine = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.BAICELLS_QRTB) # User uses the CLI tool to get eNodeB to reboot acs_state_machine.reboot_asap() # And now the Inform message arrives from the eNodeB inform_msg = \ Tr069MessageBuilder.get_qrtb_inform( params=DEFAULT_INFORM_PARAMS, oui='48BF74', enb_serial='1202000181186TB0006', event_codes=['2 PERIODIC'], ) resp = acs_state_machine.handle_tr069_message(inform_msg) self.assertTrue( isinstance(resp, models.InformResponse), 'In reboot sequence, state machine should still ' 'respond to an Inform with InformResponse.', ) req = models.DummyInput() resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(resp, models.Reboot), 'In reboot sequence, state machine should send a ' 'Reboot message.', ) req = Tr069MessageBuilder.get_reboot_response() resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(resp, models.DummyInput), 'State machine should end TR-069 session after ' 'receiving a RebootResponse', )
def _get_manager(self) -> StateMachineManager: service = EnodebAcsStateMachineBuilder.build_magma_service() return StateMachineManager(service)
def _get_acs(self): """ Get a dummy ACS statemachine for tests""" service = EnodebAcsStateMachineBuilder.build_magma_service() return DummyHandler(service)
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 _prepare_sm(): sm = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.BAICELLS_QRTB) sm.device_cfg.set_parameter(ParameterName.IP_SEC_ENABLE, False) sm.device_cfg.set_parameter(ParameterName.NUM_PLMNS, 1) return sm
def test_post_processing(self, sas_enabled, prim_src) -> None: """ Test FreedomFi One specific post processing functionality""" acs_state_machine = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.FREEDOMFI_ONE) cfg_desired = Mock() acs_state_machine.device_cfg.set_parameter( ParameterName.SERIAL_NUMBER, "2006CW5000023", ) cfg_init = FreedomFiOneConfigurationInitializer(acs_state_machine) cfg_init.postprocess( EnodebConfigBuilder.get_mconfig(), get_service_config(sas_enabled=sas_enabled, prim_src=prim_src), cfg_desired, ) expected = [ call.delete_parameter('EARFCNDL'), call.delete_parameter('DL bandwidth'), call.delete_parameter('UL bandwidth'), call.set_parameter( 'tunnel_ref', value='Device.IP.Interface.1.IPv4Address.1.', ), call.set_parameter('prim_src', prim_src), call.set_parameter('carrier_agg_enable', value=True), call.set_parameter('carrier_number', value=2), call.set_parameter('contiguous_cc', value=0), call.set_parameter('web_ui_enable', value=False), call.set_parameter('sas_enabled', sas_enabled), call.set_parameter_for_object( param_name='PLMN 1 cell reserved', value=True, object_name='PLMN 1', ), ] if sas_enabled: expected += [ call.set_parameter( 'sas_server_url', 'https://spectrum-connect.federatedwireless.com/v1.2/', ), call.set_parameter('sas_uid', 'M0LK4T'), call.set_parameter('sas_category', 'A'), call.set_parameter('sas_channel_type', 'GAA'), call.set_parameter( 'sas_cert_subject', '/C=TW/O=Sercomm/OU=WInnForum CBSD Certificate/CN=P27-SCE4255W:%s', ), call.set_parameter('sas_location', 'indoor'), call.set_parameter('sas_height_type', 'AMSL'), ] cfg_desired.assert_has_calls(expected, any_order=True) # Check without sas config service_cfg = { "tr069": { "interface": "eth1", "port": 48080, "perf_mgmt_port": 8081, "public_ip": "192.88.99.142", }, "prim_src": prim_src, "reboot_enodeb_on_mme_disconnected": True, "s1_interface": "eth1", } cfg_desired = Mock() cfg_init.postprocess( EnodebConfigBuilder.get_mconfig(), service_cfg, cfg_desired, ) expected = [ call.delete_parameter('EARFCNDL'), call.delete_parameter('DL bandwidth'), call.delete_parameter('UL bandwidth'), call.set_parameter( 'tunnel_ref', value='Device.IP.Interface.1.IPv4Address.1.', ), call.set_parameter('sas_enabled', False), call.set_parameter('prim_src', prim_src), call.set_parameter('carrier_agg_enable', value=True), call.set_parameter('carrier_number', value=2), call.set_parameter('contiguous_cc', value=0), call.set_parameter('web_ui_enable', value=False), call.set_parameter_for_object( param_name='PLMN 1 cell reserved', value=True, object_name='PLMN 1', ), ] cfg_desired.assert_has_calls(expected, any_order=True) service_cfg['web_ui_enable_list'] = ["2006CW5000023"] expected = [ call.delete_parameter('EARFCNDL'), call.delete_parameter('DL bandwidth'), call.delete_parameter('UL bandwidth'), call.set_parameter( 'tunnel_ref', value='Device.IP.Interface.1.IPv4Address.1.', ), call.set_parameter('sas_enabled', False), call.set_parameter('prim_src', prim_src), call.set_parameter('carrier_agg_enable', value=True), call.set_parameter('carrier_number', value=2), call.set_parameter('contiguous_cc', value=0), call.set_parameter('web_ui_enable', value=False), call.set_parameter('web_ui_enable', value=True), call.set_parameter_for_object( param_name='PLMN 1 cell reserved', value=True, object_name='PLMN 1', ), ] cfg_desired = Mock() cfg_init.postprocess( EnodebConfigBuilder.get_mconfig(), service_cfg, cfg_desired, ) cfg_desired.assert_has_calls(expected, any_order=True)
def test_provision(self, mock_get_state) -> None: mock_get_state.return_value = MOCK_CBSD_STATE acs_state_machine = EnodebAcsStateMachineBuilder.build_acs_state_machine( EnodebDeviceName.BAICELLS_QRTB) data_model = BaicellsQRTBTrDataModel() # Send an Inform message, wait for an InformResponse inform_msg = Tr069MessageBuilder.get_qrtb_inform( params=DEFAULT_INFORM_PARAMS, oui='48BF74', enb_serial='1202000181186TB0006', event_codes=['2 PERIODIC'], ) resp = acs_state_machine.handle_tr069_message(inform_msg) self.assertTrue( isinstance(resp, models.InformResponse), 'Should respond with an InformResponse', ) # Send an empty http request to kick off the rest of provisioning req = models.DummyInput() resp = acs_state_machine.handle_tr069_message(req) # Expect a request for transient params self.assertTrue( isinstance(resp, models.GetParameterValues), 'State machine should be requesting param values', ) should_ask_for = [p.name for p in GET_TRANSIENT_PARAMS_RESPONSE_PARAMS] self.verify_acs_asking_enb_for_params(should_ask_for, resp) # Send back some typical values req = Tr069MessageBuilder.param_values_qrtb_response( GET_TRANSIENT_PARAMS_RESPONSE_PARAMS, models.GetParameterValuesResponse, ) # And then SM should request all parameter values from the data model resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(resp, models.GetParameterValues), 'State machine should be requesting param values', ) should_ask_for = [ data_model.get_parameter(pn, ).path for pn in data_model.get_parameter_names() ] self.verify_acs_asking_enb_for_params(should_ask_for, resp) # Send back typical values req = Tr069MessageBuilder.param_values_qrtb_response( GET_PARAMS_RESPONSE_PARAMS, models.GetParameterValuesResponse, ) # SM will be requesting object parameter values resp = acs_state_machine.handle_tr069_message(req) self.assertTrue( isinstance(resp, models.GetParameterValues), 'State machine should be requesting object param vals', ) should_ask_for = [ 'Device.Services.FAPService.1.CellConfig.LTE.EPC.PLMNList.1.CellReservedForOperatorUse', 'Device.Services.FAPService.1.CellConfig.LTE.EPC.PLMNList.1.Enable', 'Device.Services.FAPService.1.CellConfig.LTE.EPC.PLMNList.1.IsPrimary', 'Device.Services.FAPService.1.CellConfig.LTE.EPC.PLMNList.1.PLMNID', ] self.verify_acs_asking_enb_for_params(should_ask_for, resp) # Send back some typical values for object parameters req = Tr069MessageBuilder.get_object_param_values_response( cell_reserved_for_operator_use='true', enable='true', is_primary='true', plmnid='00101', ) resp = acs_state_machine.handle_tr069_message(req) # All the radio responses were intentionally crafted so that they match enodebd desired config. # Therefore the provisioning ends here. The radio goes directly into end_session -> notify_dp state self.assertTrue( isinstance(resp, models.DummyInput), 'State machine should send back an empty message', ) self.assertIsInstance( acs_state_machine.state, BaicellsQRTBNotifyDPState, )