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