blob: d992e581fbbf7fa901bdaa84833b476d61c4c998 [file] [log] [blame]
Wei-Yu Chen49950b92021-11-08 19:19:18 +08001"""
2Copyright 2020 The Magma Authors.
3
4This source code is licensed under the BSD-style license found in the
5LICENSE file in the root directory of this source tree.
6
7Unless required by applicable law or agreed to in writing, software
8distributed under the License is distributed on an "AS IS" BASIS,
9WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10See the License for the specific language governing permissions and
11limitations under the License.
12"""
13
14# pylint: disable=protected-access
15from data_models.data_model_parameters import ParameterName
16from devices.device_utils import EnodebDeviceName
17from tests.test_utils.enb_acs_builder import (
18 EnodebAcsStateMachineBuilder,
19)
20from tests.test_utils.enodeb_handler import EnodebHandlerTestCase
21from tests.test_utils.tr069_msg_builder import Tr069MessageBuilder
22from tr069 import models
23
24
25class BaicellsHandlerTests(EnodebHandlerTestCase):
26 def test_initial_enb_bootup(self) -> None:
27 """
28 Baicells does not support configuration during initial bootup of
29 eNB device. This is because it is in a REM process, and we just need
30 to wait for this process to finish, ~10 minutes. Attempting to
31 configure the device during this period will cause undefined
32 behavior.
33 As a result of this, end any provisoning sessions, which we can do
34 by just sending empty HTTP responses, not even using an
35 InformResponse.
36 """
37 acs_state_machine = \
38 EnodebAcsStateMachineBuilder \
39 .build_acs_state_machine(EnodebDeviceName.BAICELLS)
40
41 # Send an Inform message
42 inform_msg = Tr069MessageBuilder.get_inform(
43 '48BF74',
44 'BaiBS_RTS_3.1.6',
45 '120200002618AGP0003',
46 ['1 BOOT'],
47 )
48 resp = acs_state_machine.handle_tr069_message(inform_msg)
49
50 self.assertTrue(
51 isinstance(resp, models.DummyInput),
52 'Should respond with an InformResponse',
53 )
54
55 def test_manual_reboot(self) -> None:
56 """
57 Test a scenario where a Magma user goes through the enodebd CLI to
58 reboot the Baicells eNodeB.
59
60 This checks the scenario where the command is not sent in the middle
61 of a TR-069 provisioning session.
62 """
63 acs_state_machine = \
64 EnodebAcsStateMachineBuilder \
65 .build_acs_state_machine(EnodebDeviceName.BAICELLS)
66
67 # User uses the CLI tool to get eNodeB to reboot
68 acs_state_machine.reboot_asap()
69
70 # And now the Inform message arrives from the eNodeB
71 inform_msg = Tr069MessageBuilder.get_inform(
72 '48BF74',
73 'BaiBS_RTS_3.1.6',
74 '120200002618AGP0003',
75 ['2 PERIODIC'],
76 )
77 resp = acs_state_machine.handle_tr069_message(inform_msg)
78 self.assertTrue(
79 isinstance(resp, models.InformResponse),
80 'In reboot sequence, state machine should still '
81 'respond to an Inform with InformResponse.',
82 )
83 req = models.DummyInput()
84 resp = acs_state_machine.handle_tr069_message(req)
85 self.assertTrue(
86 isinstance(resp, models.Reboot),
87 'In reboot sequence, state machine should send a '
88 'Reboot message.',
89 )
90 req = Tr069MessageBuilder.get_reboot_response()
91 resp = acs_state_machine.handle_tr069_message(req)
92 self.assertTrue(
93 isinstance(resp, models.DummyInput),
94 'State machine should end TR-069 session after '
95 'receiving a RebootResponse',
96 )
97
98 def test_gps_coords(self) -> None:
99 """ Check GPS coordinates are processed and stored correctly """
100 acs_state_machine = \
101 EnodebAcsStateMachineBuilder \
102 .build_acs_state_machine(EnodebDeviceName.BAICELLS)
103
104 # Send an Inform message, wait for an InformResponse
105 inform_msg = Tr069MessageBuilder.get_inform(
106 '48BF74',
107 'BaiBS_RTS_3.1.6',
108 '120200002618AGP0003',
109 ['2 PERIODIC'],
110 )
111 resp = acs_state_machine.handle_tr069_message(inform_msg)
112 self.assertTrue(
113 isinstance(resp, models.InformResponse),
114 'Should respond with an InformResponse',
115 )
116
117 # Send an empty http request to kick off the rest of provisioning
118 req = models.DummyInput()
119 resp = acs_state_machine.handle_tr069_message(req)
120
121 # Expect a request for an optional parameter, three times
122 self.assertTrue(
123 isinstance(resp, models.GetParameterValues),
124 'State machine should be requesting param values',
125 )
126 req = models.GetParameterValuesResponse()
127 param_val_list = [
128 Tr069MessageBuilder.get_parameter_value_struct(
129 name='Device.X_BAICELLS_COM_GpsSyncEnable',
130 val_type='boolean',
131 data='true',
132 ),
133 ]
134 req.ParameterList = models.ParameterValueList()
135 req.ParameterList.ParameterValueStruct = param_val_list
136 resp = acs_state_machine.handle_tr069_message(req)
137
138 self.assertTrue(
139 isinstance(resp, models.GetParameterValues),
140 'State machine should be requesting param values',
141 )
142 req = models.GetParameterValuesResponse()
143 param_val_list = [
144 Tr069MessageBuilder.get_parameter_value_struct(
145 name='Device.FAP.GPS.LockedLatitude',
146 val_type='int',
147 data='37483629',
148 ),
149 ]
150 req.ParameterList = models.ParameterValueList()
151 req.ParameterList.ParameterValueStruct = param_val_list
152 resp = acs_state_machine.handle_tr069_message(req)
153
154 self.assertTrue(
155 isinstance(resp, models.GetParameterValues),
156 'State machine should be requesting param values',
157 )
158 req = models.GetParameterValuesResponse()
159 param_val_list = [
160 Tr069MessageBuilder.get_parameter_value_struct(
161 name='Device.FAP.GPS.LockedLongitude',
162 val_type='int',
163 data='-122150583',
164 ),
165 ]
166 req.ParameterList = models.ParameterValueList()
167 req.ParameterList.ParameterValueStruct = param_val_list
168 acs_state_machine.handle_tr069_message(req)
169
170 gps_long = acs_state_machine.get_parameter(ParameterName.GPS_LONG)
171 gps_lat = acs_state_machine.get_parameter(ParameterName.GPS_LAT)
172
173 self.assertTrue(gps_long == '-122.150583', 'Should be valid longitude')
174 self.assertTrue(gps_lat == '37.483629', 'Should be valid latitude')
175
176 def test_manual_reboot_during_provisioning(self) -> None:
177 """
178 Test a scenario where a Magma user goes through the enodebd CLI to
179 reboot the Baicells eNodeB.
180
181 This checks the scenario where the command is sent in the middle
182 of a TR-069 provisioning session.
183 """
184 acs_state_machine = \
185 EnodebAcsStateMachineBuilder \
186 .build_acs_state_machine(EnodebDeviceName.BAICELLS)
187
188 # Send an Inform message, wait for an InformResponse
189 inform_msg = Tr069MessageBuilder.get_inform(
190 '48BF74',
191 'BaiBS_RTS_3.1.6',
192 '120200002618AGP0003',
193 ['2 PERIODIC'],
194 )
195 resp = acs_state_machine.handle_tr069_message(inform_msg)
196 self.assertTrue(
197 isinstance(resp, models.InformResponse),
198 'Should respond with an InformResponse',
199 )
200
201 # Send an empty http request to kick off the rest of provisioning
202 req = models.DummyInput()
203 resp = acs_state_machine.handle_tr069_message(req)
204
205 # Expect a request for an optional parameter, three times
206 self.assertTrue(
207 isinstance(resp, models.GetParameterValues),
208 'State machine should be requesting param values',
209 )
210 req = Tr069MessageBuilder.get_fault()
211
212 # User uses the CLI tool to get eNodeB to reboot
213 acs_state_machine.reboot_asap()
214
215 resp = acs_state_machine.handle_tr069_message(req)
216 self.assertTrue(
217 isinstance(resp, models.Reboot),
218 'In reboot sequence, state machine should send a '
219 'Reboot message.',
220 )
221 req = Tr069MessageBuilder.get_reboot_response()
222 resp = acs_state_machine.handle_tr069_message(req)
223 self.assertTrue(
224 isinstance(resp, models.DummyInput),
225 'State machine should end TR-069 session after '
226 'receiving a RebootResponse',
227 )
228
229 def test_missing_param_during_provisioning(self) -> None:
230 """
231 Test the scenario where:
232 - enodebd is configuring the eNodeB
233 - eNB does not send all parameters due to bug
234 """
235 acs_state_machine = \
236 EnodebAcsStateMachineBuilder \
237 .build_acs_state_machine(EnodebDeviceName.BAICELLS)
238
239 # Send an Inform message, wait for an InformResponse
240 inform_msg = Tr069MessageBuilder.get_inform()
241 resp = acs_state_machine.handle_tr069_message(inform_msg)
242 self.assertTrue(
243 isinstance(resp, models.InformResponse),
244 'Should respond with an InformResponse',
245 )
246
247 # Send an empty http request to kick off the rest of provisioning
248 req = models.DummyInput()
249 resp = acs_state_machine.handle_tr069_message(req)
250
251 # Expect a request for an optional parameter, three times
252 self.assertTrue(
253 isinstance(resp, models.GetParameterValues),
254 'State machine should be requesting param values',
255 )
256 req = Tr069MessageBuilder.get_fault()
257 resp = acs_state_machine.handle_tr069_message(req)
258 self.assertTrue(
259 isinstance(resp, models.GetParameterValues),
260 'State machine should be requesting param values',
261 )
262 req = Tr069MessageBuilder.get_fault()
263 resp = acs_state_machine.handle_tr069_message(req)
264 self.assertTrue(
265 isinstance(resp, models.GetParameterValues),
266 'State machine should be requesting param values',
267 )
268 req = Tr069MessageBuilder.get_fault()
269 resp = acs_state_machine.handle_tr069_message(req)
270
271 # Expect a request for read-only params
272 self.assertTrue(
273 isinstance(resp, models.GetParameterValues),
274 'State machine should be requesting param values',
275 )
276 req = Tr069MessageBuilder.get_read_only_param_values_response()
277
278 # Send back some typical values
279 # And then SM should request regular parameter values
280 resp = acs_state_machine.handle_tr069_message(req)
281 self.assertTrue(
282 isinstance(resp, models.GetParameterValues),
283 'State machine should be requesting param values',
284 )
285
286 # Send back typical values for the regular parameters
287 # Pretend that here the NumPLMNs was not sent because of a Baicells bug
288 req = Tr069MessageBuilder.\
289 get_regular_param_values_response(
290 admin_state=False,
291 earfcndl=39150,
292 exclude_num_plmns=True,
293 )
294 resp = acs_state_machine.handle_tr069_message(req)
295
296 # The state machine will fail and go into an error state.
297 # It will send an empty http response to end the session.
298 # Regularly, the SM should be using info on the number
299 # of PLMNs to figure out which object parameter values
300 # to fetch.
301 self.assertTrue(
302 isinstance(resp, models.DummyInput),
303 'State machine should be ending session',
304 )
305
306 def test_provision_multi_without_invasive_changes(self) -> None:
307 """
308 Test the scenario where:
309 - eNodeB has already been powered for 10 minutes without configuration
310 - Setting parameters which are 'non-invasive' on the eNodeB
311 - Using enodebd mconfig which has old style config with addition
312 of eNodeB config tied to a serial number
313
314 'Invasive' parameters are those which require special behavior to apply
315 the changes for the eNodeB.
316 """
317 acs_state_machine = \
318 EnodebAcsStateMachineBuilder \
319 .build_multi_enb_acs_state_machine(EnodebDeviceName.BAICELLS)
320
321 # Send an Inform message, wait for an InformResponse
322 inform_msg = Tr069MessageBuilder.get_inform()
323 resp = acs_state_machine.handle_tr069_message(inform_msg)
324 self.assertTrue(
325 isinstance(resp, models.InformResponse),
326 'Should respond with an InformResponse',
327 )
328
329 # Send an empty http request to kick off the rest of provisioning
330 req = models.DummyInput()
331 resp = acs_state_machine.handle_tr069_message(req)
332
333 # Expect a request for an optional parameter, three times
334 self.assertTrue(
335 isinstance(resp, models.GetParameterValues),
336 'State machine should be requesting param values',
337 )
338 req = Tr069MessageBuilder.get_fault()
339 resp = acs_state_machine.handle_tr069_message(req)
340 self.assertTrue(
341 isinstance(resp, models.GetParameterValues),
342 'State machine should be requesting param values',
343 )
344 req = Tr069MessageBuilder.get_fault()
345 resp = acs_state_machine.handle_tr069_message(req)
346 self.assertTrue(
347 isinstance(resp, models.GetParameterValues),
348 'State machine should be requesting param values',
349 )
350 req = Tr069MessageBuilder.get_fault()
351 resp = acs_state_machine.handle_tr069_message(req)
352
353 # Expect a request for read-only params
354 self.assertTrue(
355 isinstance(resp, models.GetParameterValues),
356 'State machine should be requesting param values',
357 )
358 req = Tr069MessageBuilder.get_read_only_param_values_response()
359
360 # Send back some typical values
361 # And then SM should request regular parameter values
362 resp = acs_state_machine.handle_tr069_message(req)
363 self.assertTrue(
364 isinstance(resp, models.GetParameterValues),
365 'State machine should be requesting param values',
366 )
367
368 # Send back typical values for the regular parameters
369 req = Tr069MessageBuilder.\
370 get_regular_param_values_response(
371 admin_state=False,
372 earfcndl=39150,
373 )
374 resp = acs_state_machine.handle_tr069_message(req)
375
376 # SM will be requesting object parameter values
377 self.assertTrue(
378 isinstance(resp, models.GetParameterValues),
379 'State machine should be requesting object param vals',
380 )
381
382 # Send back some typical values for object parameters
383 req = Tr069MessageBuilder.get_object_param_values_response()
384 resp = acs_state_machine.handle_tr069_message(req)
385
386 # In this scenario, the ACS and thus state machine will not need
387 # to delete or add objects to the eNB configuration.
388 # SM should then just be attempting to set parameter values
389 self.assertTrue(
390 isinstance(resp, models.SetParameterValues),
391 'State machine should be setting param values',
392 )
393
394 isEnablingAdminState = False
395 param = 'Device.Services.FAPService.1.FAPControl.LTE.AdminState'
396 for name_value in resp.ParameterList.ParameterValueStruct:
397 if name_value.Name == param:
398 isEnablingAdminState = True
399 self.assertTrue(
400 isEnablingAdminState,
401 'eNB config is set to enable transmit, '
402 'while old enodebd config does not '
403 'enable transmit. Use eNB config.',
404 )
405
406 def test_provision_without_invasive_changes(self) -> None:
407 """
408 Test the scenario where:
409 - eNodeB has already been powered for 10 minutes without configuration
410 - Setting parameters which are 'non-invasive' on the eNodeB
411
412 'Invasive' parameters are those which require special behavior to apply
413 the changes for the eNodeB.
414 """
415 acs_state_machine = \
416 EnodebAcsStateMachineBuilder \
417 .build_acs_state_machine(EnodebDeviceName.BAICELLS)
418
419 # Send an Inform message, wait for an InformResponse
420 inform_msg = Tr069MessageBuilder.get_inform()
421 resp = acs_state_machine.handle_tr069_message(inform_msg)
422 self.assertTrue(
423 isinstance(resp, models.InformResponse),
424 'Should respond with an InformResponse',
425 )
426
427 # Send an empty http request to kick off the rest of provisioning
428 req = models.DummyInput()
429 resp = acs_state_machine.handle_tr069_message(req)
430
431 # Expect a request for an optional parameter, three times
432 self.assertTrue(
433 isinstance(resp, models.GetParameterValues),
434 'State machine should be requesting param values',
435 )
436 req = Tr069MessageBuilder.get_fault()
437 resp = acs_state_machine.handle_tr069_message(req)
438 self.assertTrue(
439 isinstance(resp, models.GetParameterValues),
440 'State machine should be requesting param values',
441 )
442 req = Tr069MessageBuilder.get_fault()
443 resp = acs_state_machine.handle_tr069_message(req)
444 self.assertTrue(
445 isinstance(resp, models.GetParameterValues),
446 'State machine should be requesting param values',
447 )
448 req = Tr069MessageBuilder.get_fault()
449 resp = acs_state_machine.handle_tr069_message(req)
450
451 # Expect a request for read-only params
452 self.assertTrue(
453 isinstance(resp, models.GetParameterValues),
454 'State machine should be requesting param values',
455 )
456 req = Tr069MessageBuilder.get_read_only_param_values_response()
457
458 # Send back some typical values
459 # And then SM should request regular parameter values
460 resp = acs_state_machine.handle_tr069_message(req)
461 self.assertTrue(
462 isinstance(resp, models.GetParameterValues),
463 'State machine should be requesting param values',
464 )
465
466 # Send back typical values for the regular parameters
467 req = Tr069MessageBuilder.\
468 get_regular_param_values_response(
469 admin_state=False,
470 earfcndl=39150,
471 )
472 resp = acs_state_machine.handle_tr069_message(req)
473
474 # SM will be requesting object parameter values
475 self.assertTrue(
476 isinstance(resp, models.GetParameterValues),
477 'State machine should be requesting object param vals',
478 )
479
480 # Send back some typical values for object parameters
481 req = Tr069MessageBuilder.get_object_param_values_response()
482 resp = acs_state_machine.handle_tr069_message(req)
483
484 # In this scenario, the ACS and thus state machine will not need
485 # to delete or add objects to the eNB configuration.
486 # SM should then just be attempting to set parameter values
487 self.assertTrue(
488 isinstance(resp, models.SetParameterValues),
489 'State machine should be setting param values',
490 )
491
492 # Send back confirmation that the parameters were successfully set
493 req = models.SetParameterValuesResponse()
494 req.Status = 0
495 resp = acs_state_machine.handle_tr069_message(req)
496
497 # Expect a request for read-only params
498 self.assertTrue(
499 isinstance(resp, models.GetParameterValues),
500 'State machine should be requesting param values',
501 )
502 req = Tr069MessageBuilder.get_read_only_param_values_response()
503
504 # Send back some typical values
505 # And then SM should continue polling the read-only params
506 resp = acs_state_machine.handle_tr069_message(req)
507 self.assertTrue(
508 isinstance(resp, models.DummyInput),
509 'State machine should be ending session',
510 )
511
512 # If a different eNB is suddenly plugged in, or the same eNB sends a
513 # new Inform, enodebd should be able to handle it.
514 # Send an Inform message, wait for an InformResponse
515 inform_msg = Tr069MessageBuilder.get_inform()
516 resp = acs_state_machine.handle_tr069_message(inform_msg)
517 self.assertTrue(
518 isinstance(resp, models.InformResponse),
519 'Should respond with an InformResponse',
520 )
521
522 # Send an empty http request to kick off the rest of provisioning
523 req = models.DummyInput()
524 resp = acs_state_machine.handle_tr069_message(req)
525
526 # Expect a request for an optional parameter, three times
527 self.assertTrue(
528 isinstance(resp, models.GetParameterValues),
529 'State machine should be requesting param values',
530 )
531
532 def test_reboot_after_invasive_changes(self) -> None:
533 """
534 Test the scenario where:
535 - eNodeB has already been powered for 10 minutes without configuration
536 - Setting parameters which are 'invasive' on the eNodeB
537 - Simulate the scenario up until reboot, and test that enodebd does
538 not try to complete configuration after reboot, because it is
539 waiting for REM process to finish running
540 - This test does not wait the ten minutes to simulate REM process
541 finishing on the Baicells eNodeB
542
543 'Invasive' parameters are those which require special behavior to apply
544 the changes for the eNodeB.
545
546 In the case of the Baicells eNodeB, properly applying changes to
547 invasive parameters requires rebooting the device.
548 """
549 acs_state_machine = \
550 EnodebAcsStateMachineBuilder\
551 .build_acs_state_machine(EnodebDeviceName.BAICELLS)
552 # Since the test utils pretend the eNB is set to 20MHz, we force this
553 # to 10 MHz, so the state machine sets this value.
554 acs_state_machine.mconfig.bandwidth_mhz = 10
555
556 # Send an Inform message, wait for an InformResponse
557 inform_msg = Tr069MessageBuilder.get_inform(
558 '48BF74',
559 'BaiBS_RTS_3.1.6',
560 '120200002618AGP0003',
561 ['2 PERIODIC'],
562 )
563 resp = acs_state_machine.handle_tr069_message(inform_msg)
564 self.assertTrue(
565 isinstance(resp, models.InformResponse),
566 'Should respond with an InformResponse',
567 )
568
569 # Send an empty http request to kick off the rest of provisioning
570 req = models.DummyInput()
571 resp = acs_state_machine.handle_tr069_message(req)
572
573 # Expect a request for an optional parameter, three times
574 self.assertTrue(
575 isinstance(resp, models.GetParameterValues),
576 'State machine should be requesting param values',
577 )
578 req = Tr069MessageBuilder.get_fault()
579 resp = acs_state_machine.handle_tr069_message(req)
580 self.assertTrue(
581 isinstance(resp, models.GetParameterValues),
582 'State machine should be requesting param values',
583 )
584 req = Tr069MessageBuilder.get_fault()
585 resp = acs_state_machine.handle_tr069_message(req)
586 self.assertTrue(
587 isinstance(resp, models.GetParameterValues),
588 'State machine should be requesting param values',
589 )
590 req = Tr069MessageBuilder.get_fault()
591 resp = acs_state_machine.handle_tr069_message(req)
592
593 # Expect a request for read-only params
594 self.assertTrue(
595 isinstance(resp, models.GetParameterValues),
596 'State machine should be requesting param values',
597 )
598 req = Tr069MessageBuilder.get_read_only_param_values_response()
599
600 # Send back some typical values
601 # And then SM should request regular parameter values
602 resp = acs_state_machine.handle_tr069_message(req)
603 self.assertTrue(
604 isinstance(resp, models.GetParameterValues),
605 'State machine should be requesting param values',
606 )
607
608 # Send back typical values for the regular parameters
609 req = Tr069MessageBuilder.get_regular_param_values_response()
610 resp = acs_state_machine.handle_tr069_message(req)
611
612 # SM will be requesting object parameter values
613 self.assertTrue(
614 isinstance(resp, models.GetParameterValues),
615 'State machine should be requesting object param vals',
616 )
617
618 # Send back some typical values for object parameters
619 req = Tr069MessageBuilder.get_object_param_values_response()
620 resp = acs_state_machine.handle_tr069_message(req)
621
622 # In this scenario, the ACS and thus state machine will not need
623 # to delete or add objects to the eNB configuration.
624 # SM should then just be attempting to set parameter values
625 self.assertTrue(
626 isinstance(resp, models.SetParameterValues),
627 'State machine should be setting param values',
628 )
629
630 # Send back confirmation that the parameters were successfully set
631 req = models.SetParameterValuesResponse()
632 req.Status = 0
633 resp = acs_state_machine.handle_tr069_message(req)
634
635 # Since invasive parameters have been set, then to apply the changes
636 # to the Baicells eNodeB, we need to reboot the device
637 self.assertTrue(isinstance(resp, models.Reboot))
638 req = Tr069MessageBuilder.get_reboot_response()
639 resp = acs_state_machine.handle_tr069_message(req)
640
641 # After the reboot has been received, enodebd should end the
642 # provisioning session
643 self.assertTrue(
644 isinstance(resp, models.DummyInput),
645 'After sending command to reboot the Baicells eNodeB, '
646 'enodeb should end the TR-069 session.',
647 )
648
649 # At this point, sometime after the eNodeB reboots, we expect it to
650 # send an Inform indicating reboot. Since it should be in REM process,
651 # we hold off on finishing configuration, and end TR-069 sessions.
652 req = Tr069MessageBuilder.get_inform(
653 '48BF74', 'BaiBS_RTS_3.1.6',
654 '120200002618AGP0003',
655 ['1 BOOT', 'M Reboot'],
656 )
657 resp = acs_state_machine.handle_tr069_message(req)
658 self.assertTrue(
659 isinstance(resp, models.DummyInput),
660 'After receiving a post-reboot Inform, enodebd '
661 'should end TR-069 sessions for 10 minutes to wait '
662 'for REM process to finish.',
663 )
664
665 # Pretend that we have waited, and now we are in normal operation again
666 acs_state_machine.transition('wait_inform_post_reboot')
667 req = Tr069MessageBuilder.get_inform(
668 '48BF74', 'BaiBS_RTS_3.1.6',
669 '120200002618AGP0003',
670 ['2 PERIODIC'],
671 )
672 resp = acs_state_machine.handle_tr069_message(req)
673 self.assertTrue(
674 isinstance(resp, models.InformResponse),
675 'After receiving a post-reboot Inform, enodebd '
676 'should end TR-069 sessions for 10 minutes to wait '
677 'for REM process to finish.',
678 )
679
680 req = models.DummyInput()
681 resp = acs_state_machine.handle_tr069_message(req)
682 self.assertTrue(
683 isinstance(resp, models.GetParameterValues),
684 'enodebd should be requesting params',
685 )
686 self.assertTrue(
687 len(resp.ParameterNames.string) > 1,
688 'Should be requesting transient params.',
689 )
690
691 def test_reboot_without_getting_optional(self) -> None:
692 """
693 The state machine should not skip figuring out which optional
694 parameters are present.
695 """
696 acs_state_machine = \
697 EnodebAcsStateMachineBuilder \
698 .build_acs_state_machine(EnodebDeviceName.BAICELLS)
699
700 # Send an Inform message, wait for an InformResponse
701 inform_msg = Tr069MessageBuilder.get_inform(
702 '48BF74',
703 'BaiBS_RTS_3.1.6',
704 '120200002618AGP0003',
705 ['2 PERIODIC'],
706 )
707 resp = acs_state_machine.handle_tr069_message(inform_msg)
708 self.assertTrue(
709 isinstance(resp, models.InformResponse),
710 'Should respond with an InformResponse',
711 )
712
713 # And now reboot the eNodeB
714 acs_state_machine.transition('reboot')
715 req = models.DummyInput()
716 resp = acs_state_machine.handle_tr069_message(req)
717 self.assertTrue(isinstance(resp, models.Reboot))
718 req = Tr069MessageBuilder.get_reboot_response()
719 resp = acs_state_machine.handle_tr069_message(req)
720
721 # After the reboot has been received, enodebd should end the
722 # provisioning session
723 self.assertTrue(
724 isinstance(resp, models.DummyInput),
725 'After sending command to reboot the Baicells eNodeB, '
726 'enodeb should end the TR-069 session.',
727 )
728
729 # At this point, sometime after the eNodeB reboots, we expect it to
730 # send an Inform indicating reboot. Since it should be in REM process,
731 # we hold off on finishing configuration, and end TR-069 sessions.
732 req = Tr069MessageBuilder.get_inform(
733 '48BF74', 'BaiBS_RTS_3.1.6',
734 '120200002618AGP0003',
735 ['1 BOOT', 'M Reboot'],
736 )
737 resp = acs_state_machine.handle_tr069_message(req)
738 self.assertTrue(
739 isinstance(resp, models.DummyInput),
740 'After receiving a post-reboot Inform, enodebd '
741 'should end TR-069 sessions for 10 minutes to wait '
742 'for REM process to finish.',
743 )
744
745 # Pretend that we have waited, and now we are in normal operation again
746 acs_state_machine.transition('wait_inform_post_reboot')
747 req = Tr069MessageBuilder.get_inform(
748 '48BF74', 'BaiBS_RTS_3.1.6',
749 '120200002618AGP0003',
750 ['2 PERIODIC'],
751 )
752 resp = acs_state_machine.handle_tr069_message(req)
753 self.assertTrue(
754 isinstance(resp, models.InformResponse),
755 'After receiving a post-reboot Inform, enodebd '
756 'should end TR-069 sessions for 10 minutes to wait '
757 'for REM process to finish.',
758 )
759
760 # Since we haven't figured out the presence of optional parameters, the
761 # state machine should be requesting them now. There are three for the
762 # Baicells state machine.
763 req = models.DummyInput()
764 resp = acs_state_machine.handle_tr069_message(req)
765 self.assertTrue(
766 isinstance(resp, models.GetParameterValues),
767 'enodebd should be requesting params',
768 )
769 self.assertTrue(
770 len(resp.ParameterNames.string) == 1,
771 'Should be requesting optional params.',
772 )
773 req = Tr069MessageBuilder.get_fault()
774 resp = acs_state_machine.handle_tr069_message(req)
775 self.assertTrue(
776 isinstance(resp, models.GetParameterValues),
777 'State machine should be requesting param',
778 )
779 self.assertTrue(
780 len(resp.ParameterNames.string) == 1,
781 'Should be requesting optional params.',
782 )
783 req = Tr069MessageBuilder.get_fault()
784 resp = acs_state_machine.handle_tr069_message(req)
785 self.assertTrue(
786 isinstance(resp, models.GetParameterValues),
787 'State machine should be requesting param',
788 )
789 self.assertTrue(
790 len(resp.ParameterNames.string) == 1,
791 'Should be requesting optional params.',
792 )
793
794 def test_missing_mme_timeout_handler(self) -> None:
795 acs_state_machine = \
796 EnodebAcsStateMachineBuilder \
797 .build_acs_state_machine(EnodebDeviceName.BAICELLS)
798
799 # Send an Inform message, wait for an InformResponse
800 inform_msg = Tr069MessageBuilder.get_inform(
801 '48BF74',
802 'BaiBS_RTS_3.1.6',
803 '120200002618AGP0003',
804 ['2 PERIODIC'],
805 )
806 acs_state_machine.handle_tr069_message(inform_msg)
807 # Send an empty http request to kick off the rest of provisioning
808 req = models.DummyInput()
809 acs_state_machine.handle_tr069_message(req)
810
811 acs_state_machine.mme_timeout_handler.cancel()
812 acs_state_machine.mme_timeout_handler = None
813
814 # Send an Inform message, wait for an InformResponse
815 inform_msg = Tr069MessageBuilder.get_inform(
816 '48BF74',
817 'BaiBS_RTS_3.1.6',
818 '120200002618AGP0003',
819 ['2 PERIODIC'],
820 )
821 acs_state_machine.handle_tr069_message(inform_msg)
822
823 def test_fault_after_set_parameters(self) -> None:
824 acs_state_machine = \
825 EnodebAcsStateMachineBuilder \
826 .build_acs_state_machine(EnodebDeviceName.BAICELLS)
827
828 # Send an Inform message, wait for an InformResponse
829 inform_msg = Tr069MessageBuilder.get_inform(
830 '48BF74',
831 'BaiBS_RTS_3.1.6',
832 '120200002618AGP0003',
833 ['2 PERIODIC'],
834 )
835 resp = acs_state_machine.handle_tr069_message(inform_msg)
836 self.assertTrue(
837 isinstance(resp, models.InformResponse),
838 'Should respond with an InformResponse',
839 )
840
841 # Send an empty http request to kick off the rest of provisioning
842 req = models.DummyInput()
843 resp = acs_state_machine.handle_tr069_message(req)
844
845 # Expect a request for an optional parameter, three times
846 self.assertTrue(
847 isinstance(resp, models.GetParameterValues),
848 'State machine should be requesting param values',
849 )
850 req = Tr069MessageBuilder.get_fault()
851 resp = acs_state_machine.handle_tr069_message(req)
852 self.assertTrue(
853 isinstance(resp, models.GetParameterValues),
854 'State machine should be requesting param values',
855 )
856 req = Tr069MessageBuilder.get_fault()
857 resp = acs_state_machine.handle_tr069_message(req)
858 self.assertTrue(
859 isinstance(resp, models.GetParameterValues),
860 'State machine should be requesting param values',
861 )
862 req = Tr069MessageBuilder.get_fault()
863 resp = acs_state_machine.handle_tr069_message(req)
864
865 # Expect a request for read-only params
866 self.assertTrue(
867 isinstance(resp, models.GetParameterValues),
868 'State machine should be requesting param values',
869 )
870 req = Tr069MessageBuilder.get_read_only_param_values_response()
871
872 # Send back some typical values
873 # And then SM should request regular parameter values
874 resp = acs_state_machine.handle_tr069_message(req)
875 self.assertTrue(
876 isinstance(resp, models.GetParameterValues),
877 'State machine should be requesting param values',
878 )
879
880 # Send back typical values for the regular parameters
881 req = Tr069MessageBuilder.get_regular_param_values_response()
882 resp = acs_state_machine.handle_tr069_message(req)
883
884 # SM will be requesting object parameter values
885 self.assertTrue(
886 isinstance(resp, models.GetParameterValues),
887 'State machine should be requesting object param vals',
888 )
889
890 # Send back some typical values for object parameters
891 req = Tr069MessageBuilder.get_object_param_values_response()
892 resp = acs_state_machine.handle_tr069_message(req)
893
894 # In this scenario, the ACS and thus state machine will not need
895 # to delete or add objects to the eNB configuration.
896 # SM should then just be attempting to set parameter values
897 self.assertTrue(
898 isinstance(resp, models.SetParameterValues),
899 'State machine should be setting param values',
900 )
901
902 req = models.Fault()
903 req.FaultCode = 12345
904 req.FaultString = 'Test FaultString'
905 acs_state_machine.handle_tr069_message(req)
906 self.assertTrue(
907 'Error' in acs_state_machine.get_state(),
908 'Should be in error state',
909 )
910
911 def test_autoremediation_from_fault(self):
912 """
913 Transition the state machine into the unexpected fault state, then
914 verify that it transitions itself back to WaitInform after an Inform
915 is received.
916 """
917 sm = EnodebAcsStateMachineBuilder.build_acs_state_machine(
918 EnodebDeviceName.BAICELLS,
919 )
920
921 # Send an initial inform
922 inform_msg = Tr069MessageBuilder.get_inform(
923 '48BF74',
924 'BaiBS_RTS_3.1.6',
925 '120200002618AGP0003',
926 ['2 PERIODIC'],
927 )
928 resp = sm.handle_tr069_message(inform_msg)
929 self.assertTrue(
930 isinstance(resp, models.InformResponse),
931 'Should respond with an InformResponse',
932 )
933
934 # Now send a fault
935 req = models.Fault()
936 req.FaultCode = 12345
937 req.FaultString = 'Test FaultString'
938 sm.handle_tr069_message(req)
939 self.assertTrue('Error' in sm.get_state(), 'Should be in error state')
940
941 # Send the Inform again, verify SM transitions out of fault
942 resp = sm.handle_tr069_message(inform_msg)
943 self.assertTrue(isinstance(resp, models.DummyInput))
944 self.assertEqual('Waiting for an Inform', sm.get_state())