blob: bfed74686b0cc9abc1eb8a4c95d2b41ce6fa2900 [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 devices.device_utils import EnodebDeviceName
8from tests.test_utils.enb_acs_builder import (
9 EnodebAcsStateMachineBuilder,
10)
11from tests.test_utils.enodeb_handler import EnodebHandlerTestCase
12from tests.test_utils.tr069_msg_builder import Tr069MessageBuilder
13from tr069 import models
14
15
16class BaicellsOldHandlerTests(EnodebHandlerTestCase):
17 def test_initial_enb_bootup(self) -> None:
18 """
19 Baicells does not support configuration during initial bootup of
20 eNB device. This is because it is in a REM process, and we just need
21 to wait for this process to finish, ~10 minutes. Attempting to
22 configure the device during this period will cause undefined
23 behavior.
24 As a result of this, end any provisoning sessions, which we can do
25 by just sending empty HTTP responses, not even using an
26 InformResponse.
27 """
28 acs_state_machine = \
29 EnodebAcsStateMachineBuilder \
30 .build_acs_state_machine(EnodebDeviceName.BAICELLS_OLD)
31
32 # Send an Inform message
33 inform_msg = \
34 Tr069MessageBuilder.get_inform(
35 '48BF74',
36 'BaiStation_V100R001C00B110SPC002',
37 '120200002618AGP0003', ['1 BOOT'],
38 )
39 resp = acs_state_machine.handle_tr069_message(inform_msg)
40
41 self.assertTrue(
42 isinstance(resp, models.DummyInput),
43 'Should respond with an InformResponse',
44 )
45
46 def test_manual_reboot(self) -> None:
47 """
48 Test a scenario where a Magma user goes through the enodebd CLI to
49 reboot the Baicells eNodeB.
50
51 This checks the scenario where the command is not sent in the middle
52 of a TR-069 provisioning session.
53 """
54 acs_state_machine = \
55 EnodebAcsStateMachineBuilder \
56 .build_acs_state_machine(EnodebDeviceName.BAICELLS_OLD)
57
58 # User uses the CLI tool to get eNodeB to reboot
59 acs_state_machine.reboot_asap()
60
61 # And now the Inform message arrives from the eNodeB
62 inform_msg = \
63 Tr069MessageBuilder.get_inform(
64 '48BF74',
65 'BaiStation_V100R001C00B110SPC002',
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_manual_reboot_during_provisioning(self) -> None:
91 """
92 Test a scenario where a Magma user goes through the enodebd CLI to
93 reboot the Baicells eNodeB.
94
95 This checks the scenario where the command is sent in the middle
96 of a TR-069 provisioning session.
97 """
98 acs_state_machine = \
99 EnodebAcsStateMachineBuilder \
100 .build_acs_state_machine(EnodebDeviceName.BAICELLS_OLD)
101
102 # Send an Inform message, wait for an InformResponse
103 inform_msg = \
104 Tr069MessageBuilder.get_inform(
105 '48BF74',
106 'BaiStation_V100R001C00B110SPC002',
107 '120200002618AGP0003',
108 ['2 PERIODIC'],
109 )
110 resp = acs_state_machine.handle_tr069_message(inform_msg)
111 self.assertTrue(
112 isinstance(resp, models.InformResponse),
113 'Should respond with an InformResponse',
114 )
115
116 # Send an empty http request to kick off the rest of provisioning
117 req = models.DummyInput()
118 resp = acs_state_machine.handle_tr069_message(req)
119
120 # Expect a request for an optional parameter, three times
121 self.assertTrue(
122 isinstance(resp, models.GetParameterValues),
123 'State machine should be requesting param values',
124 )
125 req = Tr069MessageBuilder.get_fault()
126
127 # User uses the CLI tool to get eNodeB to reboot
128 acs_state_machine.reboot_asap()
129
130 resp = acs_state_machine.handle_tr069_message(req)
131 self.assertTrue(
132 isinstance(resp, models.Reboot),
133 'In reboot sequence, state machine should send a '
134 'Reboot message.',
135 )
136 req = Tr069MessageBuilder.get_reboot_response()
137 resp = acs_state_machine.handle_tr069_message(req)
138 self.assertTrue(
139 isinstance(resp, models.DummyInput),
140 'State machine should end TR-069 session after '
141 'receiving a RebootResponse',
142 )
143
144 def test_provision_without_invasive_changes(self) -> None:
145 """
146 Test the scenario where:
147 - eNodeB has already been powered for 10 minutes without configuration
148 - Setting parameters which are 'non-invasive' on the eNodeB
149
150 'Invasive' parameters are those which require special behavior to apply
151 the changes for the eNodeB.
152 """
153 acs_state_machine = \
154 EnodebAcsStateMachineBuilder \
155 .build_acs_state_machine(EnodebDeviceName.BAICELLS_OLD)
156
157 # Send an Inform message, wait for an InformResponse
158 inform_msg = \
159 Tr069MessageBuilder.get_inform(
160 '48BF74',
161 'BaiStation_V100R001C00B110SPC001',
162 '120200002618AGP0003',
163 ['2 PERIODIC'],
164 )
165 resp = acs_state_machine.handle_tr069_message(inform_msg)
166 self.assertTrue(
167 isinstance(resp, models.InformResponse),
168 'Should respond with an InformResponse',
169 )
170
171 # Send an empty http request to kick off the rest of provisioning
172 req = models.DummyInput()
173 resp = acs_state_machine.handle_tr069_message(req)
174
175 # Expect a request for an optional parameter, five times
176 self.assertTrue(
177 isinstance(resp, models.GetParameterValues),
178 'State machine should be requesting param values',
179 )
180 req = Tr069MessageBuilder.get_fault()
181 resp = acs_state_machine.handle_tr069_message(req)
182 self.assertTrue(
183 isinstance(resp, models.GetParameterValues),
184 'State machine should be requesting param values',
185 )
186 req = Tr069MessageBuilder.get_fault()
187 resp = acs_state_machine.handle_tr069_message(req)
188 self.assertTrue(
189 isinstance(resp, models.GetParameterValues),
190 'State machine should be requesting param values',
191 )
192 req = Tr069MessageBuilder.get_fault()
193 resp = acs_state_machine.handle_tr069_message(req)
194 self.assertTrue(
195 isinstance(resp, models.GetParameterValues),
196 'State machine should be requesting param values',
197 )
198 req = Tr069MessageBuilder.get_fault()
199 resp = acs_state_machine.handle_tr069_message(req)
200 self.assertTrue(
201 isinstance(resp, models.GetParameterValues),
202 'State machine should be requesting param values',
203 )
204 req = Tr069MessageBuilder.get_fault()
205 resp = acs_state_machine.handle_tr069_message(req)
206
207 # Expect a request for read-only params
208 self.assertTrue(
209 isinstance(resp, models.GetParameterValues),
210 'State machine should be requesting param values',
211 )
212 req = Tr069MessageBuilder.get_read_only_param_values_response()
213
214 # Send back some typical values
215 # And then SM should request regular parameter values
216 resp = acs_state_machine.handle_tr069_message(req)
217 self.assertTrue(
218 isinstance(resp, models.GetParameterValues),
219 'State machine should be requesting param values',
220 )
221
222 # Send back typical values for the regular parameters
223 req = Tr069MessageBuilder.\
224 get_regular_param_values_response(
225 admin_state=False,
226 earfcndl=39150,
227 )
228 resp = acs_state_machine.handle_tr069_message(req)
229
230 # SM will be requesting object parameter values
231 self.assertTrue(
232 isinstance(resp, models.GetParameterValues),
233 'State machine should be requesting object param vals',
234 )
235
236 # Send back some typical values for object parameters
237 req = Tr069MessageBuilder.get_object_param_values_response()
238 resp = acs_state_machine.handle_tr069_message(req)
239
240 # In this scenario, the ACS and thus state machine will not need
241 # to delete or add objects to the eNB configuration.
242 # SM should then just be attempting to set parameter values
243 self.assertTrue(
244 isinstance(resp, models.SetParameterValues),
245 'State machine should be setting param values',
246 )
247
248 # Send back confirmation that the parameters were successfully set
249 req = models.SetParameterValuesResponse()
250 req.Status = 0
251 resp = acs_state_machine.handle_tr069_message(req)
252
253 # Expect a request for read-only params
254 self.assertTrue(
255 isinstance(resp, models.GetParameterValues),
256 'State machine should be requesting param values',
257 )
258 req = Tr069MessageBuilder.get_read_only_param_values_response()
259
260 # Send back some typical values
261 # And then SM should continue polling the read-only params
262 resp = acs_state_machine.handle_tr069_message(req)
263 self.assertTrue(
264 isinstance(resp, models.DummyInput),
265 'State machine should be ending session',
266 )
267
268 # If a different eNB is suddenly plugged in, or the same eNB sends a
269 # new Inform, enodebd should be able to handle it.
270 # Send an Inform message, wait for an InformResponse
271 inform_msg = \
272 Tr069MessageBuilder.get_inform(
273 '48BF74',
274 'BaiStation_V100R001C00B110SPC002',
275 '120200002618AGP0003',
276 ['2 PERIODIC'],
277 )
278 resp = acs_state_machine.handle_tr069_message(inform_msg)
279 self.assertTrue(
280 isinstance(resp, models.InformResponse),
281 'Should respond with an InformResponse',
282 )
283
284 # Send an empty http request to kick off the rest of provisioning
285 req = models.DummyInput()
286 resp = acs_state_machine.handle_tr069_message(req)
287
288 # Expect a request for an optional parameter, three times
289 self.assertTrue(
290 isinstance(resp, models.GetParameterValues),
291 'State machine should be requesting param values',
292 )
293
294 def test_reboot_after_invasive_changes(self) -> None:
295 """
296 Test the scenario where:
297 - eNodeB has already been powered for 10 minutes without configuration
298 - Setting parameters which are 'invasive' on the eNodeB
299 - Simulate the scenario up until reboot, and test that enodebd does
300 not try to complete configuration after reboot, because it is
301 waiting for REM process to finish running
302 - This test does not wait the ten minutes to simulate REM process
303 finishing on the Baicells eNodeB
304
305 'Invasive' parameters are those which require special behavior to apply
306 the changes for the eNodeB.
307
308 In the case of the Baicells eNodeB, properly applying changes to
309 invasive parameters requires rebooting the device.
310 """
311 acs_state_machine = \
312 EnodebAcsStateMachineBuilder\
313 .build_acs_state_machine(EnodebDeviceName.BAICELLS_OLD)
314
315 # Send an Inform message, wait for an InformResponse
316 inform_msg = \
317 Tr069MessageBuilder.get_inform(
318 '48BF74',
319 'BaiStation_V100R001C00B110SPC002',
320 '120200002618AGP0003',
321 ['2 PERIODIC'],
322 )
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, five 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 self.assertTrue(
353 isinstance(resp, models.GetParameterValues),
354 'State machine should be requesting param values',
355 )
356 req = Tr069MessageBuilder.get_fault()
357 resp = acs_state_machine.handle_tr069_message(req)
358 self.assertTrue(
359 isinstance(resp, models.GetParameterValues),
360 'State machine should be requesting param values',
361 )
362 req = Tr069MessageBuilder.get_fault()
363 resp = acs_state_machine.handle_tr069_message(req)
364
365 # Expect a request for read-only params
366 self.assertTrue(
367 isinstance(resp, models.GetParameterValues),
368 'State machine should be requesting param values',
369 )
370 req = Tr069MessageBuilder.get_read_only_param_values_response()
371
372 # Send back some typical values
373 # And then SM should request regular parameter values
374 resp = acs_state_machine.handle_tr069_message(req)
375 self.assertTrue(
376 isinstance(resp, models.GetParameterValues),
377 'State machine should be requesting param values',
378 )
379
380 # Send back typical values for the regular parameters
381 req = Tr069MessageBuilder.get_regular_param_values_response()
382 resp = acs_state_machine.handle_tr069_message(req)
383
384 # SM will be requesting object parameter values
385 self.assertTrue(
386 isinstance(resp, models.GetParameterValues),
387 'State machine should be requesting object param vals',
388 )
389
390 # Send back some typical values for object parameters
391 req = Tr069MessageBuilder.get_object_param_values_response()
392 resp = acs_state_machine.handle_tr069_message(req)
393
394 # In this scenario, the ACS and thus state machine will not need
395 # to delete or add objects to the eNB configuration.
396 # SM should then just be attempting to set parameter values
397 self.assertTrue(
398 isinstance(resp, models.SetParameterValues),
399 'State machine should be setting param values',
400 )
401
402 # Send back confirmation that the parameters were successfully set
403 req = models.SetParameterValuesResponse()
404 req.Status = 0
405 resp = acs_state_machine.handle_tr069_message(req)
406
407 # Since invasive parameters have been set, then to apply the changes
408 # to the Baicells eNodeB, we need to reboot the device
409 self.assertTrue(isinstance(resp, models.Reboot))
410 req = Tr069MessageBuilder.get_reboot_response()
411 resp = acs_state_machine.handle_tr069_message(req)
412
413 # After the reboot has been received, enodebd should end the
414 # provisioning session
415 self.assertTrue(
416 isinstance(resp, models.DummyInput),
417 'After sending command to reboot the Baicells eNodeB, '
418 'enodeb should end the TR-069 session.',
419 )
420
421 # At this point, sometime after the eNodeB reboots, we expect it to
422 # send an Inform indicating reboot. Since it should be in REM process,
423 # we hold off on finishing configuration, and end TR-069 sessions.
424 req = \
425 Tr069MessageBuilder.get_inform(
426 '48BF74',
427 'BaiStation_V100R001C00B110SPC002',
428 '120200002618AGP0003',
429 ['1 BOOT', 'M Reboot'],
430 )
431 resp = acs_state_machine.handle_tr069_message(req)
432 self.assertTrue(
433 isinstance(resp, models.DummyInput),
434 'After receiving a post-reboot Inform, enodebd '
435 'should end TR-069 sessions for 10 minutes to wait '
436 'for REM process to finish.',
437 )
438
439 # Pretend that we have waited, and now we are in normal operation again
440 acs_state_machine.transition('wait_inform_post_reboot')
441 req = \
442 Tr069MessageBuilder.get_inform(
443 '48BF74',
444 'BaiStation_V100R001C00B110SPC002',
445 '120200002618AGP0003',
446 ['2 PERIODIC'],
447 )
448 resp = acs_state_machine.handle_tr069_message(req)
449 self.assertTrue(
450 isinstance(resp, models.InformResponse),
451 'After receiving a post-reboot Inform, enodebd '
452 'should end TR-069 sessions for 10 minutes to wait '
453 'for REM process to finish.',
454 )
455
456 req = models.DummyInput()
457 resp = acs_state_machine.handle_tr069_message(req)
458 self.assertTrue(
459 isinstance(resp, models.GetParameterValues),
460 'enodebd should be requesting params',
461 )
462 self.assertTrue(
463 len(resp.ParameterNames.string) > 1,
464 'Should be requesting transient params.',
465 )
466
467 def test_reboot_without_getting_optional(self) -> None:
468 """
469 The state machine should not skip figuring out which optional
470 parameters are present.
471 """
472 acs_state_machine = \
473 EnodebAcsStateMachineBuilder \
474 .build_acs_state_machine(EnodebDeviceName.BAICELLS_OLD)
475
476 # Send an Inform message, wait for an InformResponse
477 inform_msg = \
478 Tr069MessageBuilder.get_inform(
479 '48BF74',
480 'BaiStation_V100R001C00B110SPC002',
481 '120200002618AGP0003',
482 ['2 PERIODIC'],
483 )
484 resp = acs_state_machine.handle_tr069_message(inform_msg)
485 self.assertTrue(
486 isinstance(resp, models.InformResponse),
487 'Should respond with an InformResponse',
488 )
489
490 # And now reboot the eNodeB
491 acs_state_machine.transition('reboot')
492 req = models.DummyInput()
493 resp = acs_state_machine.handle_tr069_message(req)
494 self.assertTrue(isinstance(resp, models.Reboot))
495 req = Tr069MessageBuilder.get_reboot_response()
496 resp = acs_state_machine.handle_tr069_message(req)
497
498 # After the reboot has been received, enodebd should end the
499 # provisioning session
500 self.assertTrue(
501 isinstance(resp, models.DummyInput),
502 'After sending command to reboot the Baicells eNodeB, '
503 'enodeb should end the TR-069 session.',
504 )
505
506 # At this point, sometime after the eNodeB reboots, we expect it to
507 # send an Inform indicating reboot. Since it should be in REM process,
508 # we hold off on finishing configuration, and end TR-069 sessions.
509 req = \
510 Tr069MessageBuilder.get_inform(
511 '48BF74',
512 'BaiStation_V100R001C00B110SPC002',
513 '120200002618AGP0003',
514 ['1 BOOT', 'M Reboot'],
515 )
516 resp = acs_state_machine.handle_tr069_message(req)
517 self.assertTrue(
518 isinstance(resp, models.DummyInput),
519 'After receiving a post-reboot Inform, enodebd '
520 'should end TR-069 sessions for 10 minutes to wait '
521 'for REM process to finish.',
522 )
523
524 # Pretend that we have waited, and now we are in normal operation again
525 acs_state_machine.transition('wait_inform_post_reboot')
526 req = \
527 Tr069MessageBuilder.get_inform(
528 '48BF74',
529 'BaiStation_V100R001C00B110SPC002',
530 '120200002618AGP0003',
531 ['2 PERIODIC'],
532 )
533 resp = acs_state_machine.handle_tr069_message(req)
534 self.assertTrue(
535 isinstance(resp, models.InformResponse),
536 'After receiving a post-reboot Inform, enodebd '
537 'should end TR-069 sessions for 10 minutes to wait '
538 'for REM process to finish.',
539 )
540
541 # Since we haven't figured out the presence of optional parameters, the
542 # state machine should be requesting them now. There are three for the
543 # Baicells state machine.
544 req = models.DummyInput()
545 resp = acs_state_machine.handle_tr069_message(req)
546 self.assertTrue(
547 isinstance(resp, models.GetParameterValues),
548 'enodebd should be requesting params',
549 )
550 self.assertTrue(
551 len(resp.ParameterNames.string) == 1,
552 'Should be requesting optional params.',
553 )
554 req = Tr069MessageBuilder.get_fault()
555 resp = acs_state_machine.handle_tr069_message(req)
556 self.assertTrue(
557 isinstance(resp, models.GetParameterValues),
558 'State machine should be requesting param',
559 )
560 self.assertTrue(
561 len(resp.ParameterNames.string) == 1,
562 'Should be requesting optional params.',
563 )
564 req = Tr069MessageBuilder.get_fault()
565 resp = acs_state_machine.handle_tr069_message(req)
566 self.assertTrue(
567 isinstance(resp, models.GetParameterValues),
568 'State machine should be requesting param',
569 )
570 self.assertTrue(
571 len(resp.ParameterNames.string) == 1,
572 'Should be requesting optional params.',
573 )