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