blob: 72df82139b720e5160c63c3ba3382a9209090327 [file] [log] [blame]
Wei-Yu Chenb91af852022-03-15 22:24:49 +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
5
6import logging
7from typing import Any, Callable, Dict, List, Optional, Type
8
9from common.service import MagmaService
10from data_models import transform_for_magma
11from data_models.data_model import (
12 DataModel,
13 InvalidTrParamPath,
14 TrParam,
15)
16from data_models.data_model_parameters import (
17 ParameterName,
18 TrParameterType,
19)
20from configuration.service_configs import load_enb_config
21from device_config.configuration_init import build_desired_config
22from device_config.enodeb_config_postprocessor import EnodebConfigurationPostProcessor
23from device_config.enodeb_configuration import EnodebConfiguration
24from devices.device_utils import EnodebDeviceName
25from logger import EnodebdLogger
26from state_machines.acs_state_utils import (
27 get_all_objects_to_add,
28 get_all_objects_to_delete,
29 get_all_param_values_to_set,
30 get_params_to_get,
31 parse_get_parameter_values_response,
32)
33from state_machines.enb_acs import EnodebAcsStateMachine
34from state_machines.enb_acs_impl import BasicEnodebAcsStateMachine
35from state_machines.enb_acs_states import (
36 AcsMsgAndTransition,
37 AcsReadMsgResult,
38 AddObjectsState,
39 DeleteObjectsState,
40 EnbSendRebootState,
41 EndSessionState,
42 EnodebAcsState,
43 ErrorState,
44 DownloadState,
45 WaitDownloadResponseState,
46 WaitInformTransferCompleteState,
47 GetParametersState,
48 SetParameterValuesState,
49 WaitGetParametersState,
50 WaitInformMRebootState,
51 WaitInformState,
52 WaitRebootResponseState,
53 WaitSetParameterValuesState,
54 CheckStatusState,
55)
56from tr069 import models
57
58
59FAPSERVICE_PATH = "Device.Services.FAPService.1."
60FAP_CONTROL = FAPSERVICE_PATH + "FAPControl."
61
62
63class SercommHandler(BasicEnodebAcsStateMachine):
64 def __init__(self, service: MagmaService,) -> None:
65 self._state_map = {}
66 super().__init__(service=service, use_param_key=True)
67
68 def reboot_asap(self) -> None:
69 self.transition("reboot")
70
71 def is_enodeb_connected(self) -> bool:
72 return not isinstance(self.state, WaitInformState)
73
74 def _init_state_map(self) -> None:
75 self._state_map = {
76 # Inform comes in -> Respond with InformResponse
77 "wait_inform": WaitInformState(self, when_done="get_rpc_methods"),
78 # If first inform after boot -> GetRpc request comes in, if not
79 # empty request comes in => Transition to get_transient_params
80 "get_rpc_methods": SercommGetInitState(
81 self, when_done="get_transient_params",
82 ),
83 # Read transient readonly params.
84 "get_transient_params": SercommSendGetTransientParametersState(
85 self, when_upgrade="firmware_upgrade", when_done="get_params",
86 ),
87 "firmware_upgrade": DownloadState(self, when_done="wait_download_response"),
88 "wait_download_response": WaitDownloadResponseState(
89 self, when_done="wait_transfer_complete"
90 ),
91 "wait_transfer_complete": WaitInformTransferCompleteState(
92 self,
93 when_done="get_params",
94 when_periodic="wait_transfer_complete",
95 when_timeout="end_session",
96 ),
97 "get_params": SercommGetObjectParametersState(
98 self,
99 when_delete="delete_objs",
100 when_add="add_objs",
101 when_set="set_params",
102 when_skip="check_status",
103 ),
104 "delete_objs": DeleteObjectsState(
105 self, when_add="add_objs", when_skip="set_params",
106 ),
107 "add_objs": AddObjectsState(self, when_done="set_params"),
108 "set_params": SetParameterValuesState(self, when_done="wait_set_params",),
109 "wait_set_params": WaitSetParameterValuesState(
110 self,
111 when_done="check_get_params",
112 when_apply_invasive="check_get_params",
113 status_non_zero_allowed=True,
114 ),
115 "check_get_params": GetParametersState(
116 self, when_done="check_wait_get_params", request_all_params=True,
117 ),
118 "check_wait_get_params": WaitGetParametersState(
119 self, when_done="check_status",
120 ),
121 "check_status": CheckStatusState(self, when_done="check_status"),
122 "end_session": EndSessionState(self),
123 # These states are only entered through manual user intervention
124 "reboot": EnbSendRebootState(self, when_done="wait_reboot"),
125 "wait_reboot": WaitRebootResponseState(
126 self, when_done="wait_post_reboot_inform",
127 ),
128 "wait_post_reboot_inform": WaitInformMRebootState(
129 self, when_done="wait_empty", when_timeout="wait_inform",
130 ),
131 # The states below are entered when an unexpected message type is
132 # received
133 "unexpected_fault": ErrorState(
134 self, inform_transition_target="wait_inform",
135 ),
136 }
137
138 @property
139 def device_name(self) -> str:
140 return EnodebDeviceName.SERCOMM
141
142 @property
143 def data_model_class(self) -> Type[DataModel]:
144 return SercommTrDataModel
145
146 @property
147 def config_postprocessor(self) -> EnodebConfigurationPostProcessor:
148 return SercommConfigurationInitializer(self)
149
150 @property
151 def state_map(self) -> Dict[str, EnodebAcsState]:
152 return self._state_map
153
154 @property
155 def disconnected_state_name(self) -> str:
156 return "wait_inform"
157
158 @property
159 def unexpected_fault_state_name(self) -> str:
160 return "unexpected_fault"
161
162
163class SASParameters:
164 """ Class modeling the SAS parameters and their TR path"""
165
166 # For CBRS radios we set this to the limit and the SAS can reduce the
167 # power if needed.
168
169 SAS_PARAMETERS = {
170 ParameterName.SAS_ENABLE: TrParam(
171 FAP_CONTROL + "LTE.X_SCM_SAS.Enable",
172 is_invasive=False,
173 type=TrParameterType.BOOLEAN,
174 is_optional=False,
175 ),
176 ParameterName.SAS_SERVER_URL: TrParam(
177 FAP_CONTROL + "LTE.X_SCM_SAS.Server",
178 is_invasive=False,
179 type=TrParameterType.STRING,
180 is_optional=False,
181 ),
182 ParameterName.SAS_UID: TrParam(
183 FAP_CONTROL + "LTE.X_SCM_SAS.UserContactInformation",
184 is_invasive=False,
185 type=TrParameterType.STRING,
186 is_optional=False,
187 ),
188 ParameterName.SAS_CATEGORY: TrParam(
189 FAP_CONTROL + "LTE.X_SCM_SAS.Category",
190 is_invasive=False,
191 type=TrParameterType.STRING,
192 is_optional=False,
193 ),
194 ParameterName.SAS_CHANNEL_TYPE: TrParam(
195 FAP_CONTROL + "LTE.X_SCM_SAS.ProtectionLevel",
196 is_invasive=False,
197 type=TrParameterType.STRING,
198 is_optional=False,
199 ),
200 ParameterName.SAS_CERT_SUBJECT: TrParam(
201 FAP_CONTROL + "LTE.X_SCM_SAS.CertSubject",
202 is_invasive=False,
203 type=TrParameterType.STRING,
204 is_optional=False,
205 ),
206 # SAS_IC_GROUP_ID: TrParam(
207 # FAP_CONTROL + 'LTE.X_SCM_SAS.ICGGroupId', is_invasive=False,
208 # type=TrParameterType.STRING, False),
209 ParameterName.SAS_LOCATION: TrParam(
210 FAP_CONTROL + "LTE.X_SCM_SAS.Location",
211 is_invasive=False,
212 type=TrParameterType.STRING,
213 is_optional=False,
214 ),
215 ParameterName.SAS_HEIGHT_TYPE: TrParam(
216 FAP_CONTROL + "LTE.X_SCM_SAS.HeightType",
217 is_invasive=False,
218 type=TrParameterType.STRING,
219 is_optional=False,
220 ),
221 ParameterName.SAS_CPI_ENABLE: TrParam(
222 FAP_CONTROL + "LTE.X_SCM_SAS.CPIEnable",
223 is_invasive=False,
224 type=TrParameterType.BOOLEAN,
225 is_optional=False,
226 ),
227 ParameterName.SAS_CPI_IPE: TrParam(
228 FAP_CONTROL + "LTE.X_SCM_SAS.CPIInstallParamSuppliedEnable",
229 is_invasive=False,
230 type=TrParameterType.BOOLEAN,
231 is_optional=False,
232 ),
233 ParameterName.SAS_FCCID: TrParam(
234 FAP_CONTROL + "LTE.X_SCM_SAS.FCCIdentificationNumber",
235 is_invasive=False,
236 type=TrParameterType.STRING,
237 is_optional=False,
238 ),
239 ParameterName.SAS_MEAS_CAPS: TrParam(
240 FAP_CONTROL + "LTE.X_SCM_SAS.MeasCapability",
241 is_invasive=False,
242 type=TrParameterType.STRING,
243 is_optional=False,
244 ),
245 ParameterName.SAS_MANU_ENABLE: TrParam(
246 FAP_CONTROL + "LTE.X_SCM_SAS.ManufacturerPrefixEnable",
247 is_invasive=False,
248 type=TrParameterType.BOOLEAN,
249 is_optional=False,
250 ),
251 ParameterName.SAS_CPI_NAME: TrParam(
252 FAP_CONTROL + "LTE.X_SCM_SAS.CPIName",
253 is_invasive=False,
254 type=TrParameterType.STRING,
255 is_optional=False,
256 ),
257 ParameterName.SAS_CPI_ID: TrParam(
258 FAP_CONTROL + "LTE.X_SCM_SAS.CPIId",
259 is_invasive=False,
260 type=TrParameterType.STRING,
261 is_optional=False,
262 ),
263 ParameterName.SAS_ANTA_AZIMUTH: TrParam(
264 FAP_CONTROL + "LTE.X_SCM_SAS.AntennaAzimuth",
265 is_invasive=False,
266 type=TrParameterType.INT,
267 is_optional=False,
268 ),
269 ParameterName.SAS_ANTA_DOWNTILT: TrParam(
270 FAP_CONTROL + "LTE.X_SCM_SAS.AntennaDowntilt",
271 is_invasive=False,
272 type=TrParameterType.INT,
273 is_optional=False,
274 ),
275 ParameterName.SAS_ANTA_GAIN: TrParam(
276 FAP_CONTROL + "LTE.X_SCM_SAS.AntennaGain",
277 is_invasive=False,
278 type=TrParameterType.INT,
279 is_optional=False,
280 ),
281 ParameterName.SAS_ANTA_BEAMWIDTH: TrParam(
282 FAP_CONTROL + "LTE.X_SCM_SAS.AntennaBeamwidth",
283 is_invasive=False,
284 type=TrParameterType.INT,
285 is_optional=False,
286 ),
287 ParameterName.SAS_CPI_DATA: TrParam(
288 FAP_CONTROL + "LTE.X_SCM_SAS.CPISignatureData",
289 is_invasive=False,
290 type=TrParameterType.STRING,
291 is_optional=False,
292 ),
293 }
294
295
296class StatusParameters:
297 """
298 Stateful class that converts eNB status to Magma understood status.
299 Sercomm has many status params that interact with each other.
300 This class maintains the business logic of parsing these status params
301 and converting it to Magma understood fields.
302 """
303
304 # Status parameters
305 DEFAULT_GW = "defaultGW"
306 SYNC_STATUS = "syncStatus"
307 SAS_STATUS = "sasStatus"
308 ENB_STATUS = "enbStatus"
309 GPS_SCAN_STATUS = "gpsScanStatus"
310
311 STATUS_PARAMETERS = {
312 # Status nodes
313 DEFAULT_GW: TrParam(
314 "Device.X_SCM_DeviceFeature.X_SCM_NEStatus.X_SCM_DEFGW_Status",
315 is_invasive=False,
316 type=TrParameterType.STRING,
317 is_optional=False,
318 ),
319 SAS_STATUS: TrParam(
320 "Device.Services.FAPService.1.FAPControl.LTE.X_SCM_SAS.State",
321 is_invasive=False,
322 type=TrParameterType.STRING,
323 is_optional=False,
324 ),
325 GPS_SCAN_STATUS: TrParam(
326 "Device.FAP.GPS.ScanStatus",
327 is_invasive=False,
328 type=TrParameterType.STRING,
329 is_optional=False,
330 ),
331 ParameterName.GPS_LAT: TrParam(
332 "Device.FAP.GPS.LockedLatitude",
333 is_invasive=False,
334 type=TrParameterType.STRING,
335 is_optional=False,
336 ),
337 ParameterName.GPS_LONG: TrParam(
338 "Device.FAP.GPS.LockedLongitude",
339 is_invasive=False,
340 type=TrParameterType.STRING,
341 is_optional=False,
342 ),
343 ParameterName.SW_VERSION: TrParam(
344 "Device.DeviceInfo.SoftwareVersion",
345 is_invasive=False,
346 type=TrParameterType.STRING,
347 is_optional=False,
348 ),
349 ParameterName.SERIAL_NUMBER: TrParam(
350 "Device.DeviceInfo.SerialNumber",
351 is_invasive=False,
352 type=TrParameterType.STRING,
353 is_optional=False,
354 ),
355 }
356
357 # Derived status params that don't have tr69 representation.
358 DERIVED_STATUS_PARAMETERS = {
359 ParameterName.RF_TX_STATUS: TrParam(
360 "Device.X_SCM_DeviceFeature.SystemParams.RunControl.Service.4.Status",
361 is_invasive=False,
362 type=TrParameterType.BOOLEAN,
363 is_optional=False,
364 ),
365 ParameterName.GPS_STATUS: TrParam(
366 "Device.FAP.GPS.ScanStatus",
367 is_invasive=False,
368 type=TrParameterType.BOOLEAN,
369 is_optional=False,
370 ),
371 ParameterName.PTP_STATUS: TrParam(
372 InvalidTrParamPath,
373 is_invasive=False,
374 type=TrParameterType.BOOLEAN,
375 is_optional=False,
376 ),
377 ParameterName.MME_STATUS: TrParam(
378 InvalidTrParamPath, # FAP_CONTROL + "LTE.X_SCM_S1State",
379 is_invasive=False,
380 type=TrParameterType.BOOLEAN,
381 is_optional=False,
382 ),
383 ParameterName.OP_STATE: TrParam(
384 "Device.X_SCM_DeviceFeature.SystemParams.RunControl.Service.4.Status",
385 is_invasive=False,
386 type=TrParameterType.BOOLEAN,
387 is_optional=False,
388 ),
389 }
390
391 @classmethod
392 def set_magma_device_cfg(
393 cls, name_to_val: Dict, device_cfg: EnodebConfiguration,
394 ):
395 """
396 Convert Sercomm name_to_val representation to magma device_cfg
397 """
398 success_str = "SUCCESS" # String constant returned by radio
399 insync_str = "INSYNC"
400
401 if (
402 name_to_val.get(cls.DEFAULT_GW)
403 and name_to_val[cls.DEFAULT_GW].upper() != success_str
404 ):
405 # Nothing will proceed if the eNB doesn't have an IP on the WAN
406 serial_num = "unknown"
407 if device_cfg.has_parameter(ParameterName.SERIAL_NUMBER):
408 serial_num = device_cfg.get_parameter(ParameterName.SERIAL_NUMBER,)
409 EnodebdLogger.error(
410 "Radio with serial number %s doesn't have IP address " "on WAN",
411 serial_num,
412 )
413 device_cfg.set_parameter(
414 param_name=ParameterName.RF_TX_STATUS, value=False,
415 )
416 device_cfg.set_parameter(
417 param_name=ParameterName.GPS_STATUS, value=False,
418 )
419 device_cfg.set_parameter(
420 param_name=ParameterName.PTP_STATUS, value=False,
421 )
422 device_cfg.set_parameter(
423 param_name=ParameterName.MME_STATUS, value=False,
424 )
425 device_cfg.set_parameter(
426 param_name=ParameterName.OP_STATE, value=False,
427 )
428 return
429
430 if (
431 name_to_val.get(cls.SAS_STATUS)
432 and name_to_val[cls.SAS_STATUS].upper() == success_str
433 ):
434 device_cfg.set_parameter(
435 param_name=ParameterName.RF_TX_STATUS, value=True,
436 )
437 else:
438 # No sas grant so not transmitting. There is no explicit node for Tx
439 # in Sercomm
440 device_cfg.set_parameter(
441 param_name=ParameterName.RF_TX_STATUS, value=False,
442 )
443
444 if (
445 name_to_val.get(cls.GPS_SCAN_STATUS)
446 and name_to_val[cls.GPS_SCAN_STATUS].upper() == success_str
447 ):
448 device_cfg.set_parameter(
449 param_name=ParameterName.GPS_STATUS, value=True,
450 )
451 # Time comes through GPS so can only be insync with GPS is
452 # in sync, we use PTP_STATUS field to overload timer is in Sync.
453 if (
454 name_to_val.get(cls.SYNC_STATUS)
455 and name_to_val[cls.SYNC_STATUS].upper() == insync_str
456 ):
457 device_cfg.set_parameter(
458 param_name=ParameterName.PTP_STATUS, value=True,
459 )
460 else:
461 device_cfg.set_parameter(
462 param_name=ParameterName.PTP_STATUS, value=False,
463 )
464 else:
465 device_cfg.set_parameter(
466 param_name=ParameterName.GPS_STATUS, value=False,
467 )
468 device_cfg.set_parameter(
469 param_name=ParameterName.PTP_STATUS, value=False,
470 )
471
472 if (
473 name_to_val.get(cls.DEFAULT_GW)
474 and name_to_val[cls.DEFAULT_GW].upper() == success_str
475 ):
476 device_cfg.set_parameter(
477 param_name=ParameterName.MME_STATUS, value=True,
478 )
479 else:
480 device_cfg.set_parameter(
481 param_name=ParameterName.MME_STATUS, value=False,
482 )
483
484 if (
485 name_to_val.get(cls.ENB_STATUS)
486 and name_to_val[cls.ENB_STATUS].upper() == success_str
487 ):
488 device_cfg.set_parameter(
489 param_name=ParameterName.OP_STATE, value=True,
490 )
491 else:
492 device_cfg.set_parameter(
493 param_name=ParameterName.OP_STATE, value=False,
494 )
495
496 pass_through_params = [ParameterName.GPS_LAT, ParameterName.GPS_LONG]
497
498 print(name_to_val)
499 for name in pass_through_params:
500 device_cfg.set_parameter(name, name_to_val[name])
501
502
503class SercommMiscParameters:
504 """
505 Default set of parameters that enable carrier aggregation and other
506 miscellaneous properties
507 """
508
509 MISC_PARAMETERS = {
510 ParameterName.CARRIER_AGG_ENABLE: TrParam(
511 FAP_CONTROL + "LTE.X_SCM_RRMConfig.X_SCM_CA_Enable",
512 is_invasive=False,
513 type=TrParameterType.BOOLEAN,
514 is_optional=False,
515 ),
516 ParameterName.CARRIER_NUMBER: TrParam(
517 FAP_CONTROL + "LTE.X_SCM_RRMConfig.X_SCM_Cell_Number",
518 is_invasive=False,
519 type=TrParameterType.INT,
520 is_optional=False,
521 ),
522 ParameterName.CONTIGUOUS_CC: TrParam(
523 FAP_CONTROL + "LTE.X_SCM_RRMConfig.X_SCM_CELL_Freq_Contiguous",
524 is_invasive=False,
525 type=TrParameterType.INT,
526 is_optional=False,
527 ),
528 ParameterName.PRIM_SOURCE: TrParam(
529 FAPSERVICE_PATH + "REM.X_SCM_tfcsManagerConfig.primSrc",
530 is_invasive=False,
531 type=TrParameterType.STRING,
532 is_optional=False,
533 ),
534 ParameterName.CWMP_ENABLE: TrParam(
535 "Device.ManagementServer.EnableCWMP",
536 is_invasive=False,
537 type=TrParameterType.BOOLEAN,
538 is_optional=False,
539 ),
540 }
541
542
543class SercommTrDataModel(DataModel):
544 """
545 Class to represent relevant data model parameters from TR-196/TR-098.
546 This class is effectively read-only.
547
548 These models have these idiosyncrasies (on account of running TR098):
549
550 - Parameter content root is different (InternetGatewayDevice)
551 - GetParameter queries with a wildcard e.g. InternetGatewayDevice. do
552 not respond with the full tree (we have to query all parameters)
553 - MME status is not exposed - we assume the MME is connected if
554 the eNodeB is transmitting (OpState=true)
555 - Parameters such as band capability/duplex config
556 are rooted under `boardconf.` and not the device config root
557 - Num PLMNs is not reported by these units
558 """
559
560 PARAMETERS = {
561 # Top-level objects
562 ParameterName.DEVICE: TrParam(
563 "Device.",
564 is_invasive=False,
565 type=TrParameterType.OBJECT,
566 is_optional=False,
567 ),
568 ParameterName.FAP_SERVICE: TrParam(
569 FAP_CONTROL,
570 is_invasive=False,
571 type=TrParameterType.OBJECT,
572 is_optional=False,
573 ),
574 # Device info
575 ParameterName.IP_ADDRESS: TrParam(
576 "Device.IP.Interface.1.IPv4Address.1.IPAddress",
577 is_invasive=False,
578 type=TrParameterType.STRING,
579 is_optional=False,
580 ),
581 # RF-related parameters
582 ParameterName.FREQ_BAND_1: TrParam(
583 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.FreqBandIndicator",
584 is_invasive=False,
585 type=TrParameterType.UNSIGNED_INT,
586 is_optional=False,
587 ),
588 ParameterName.FREQ_BAND_2: TrParam(
589 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.X_SCM_FreqBandIndicator2",
590 is_invasive=False,
591 type=TrParameterType.UNSIGNED_INT,
592 is_optional=False,
593 ),
594 ParameterName.FREQ_BAND_LIST: TrParam(
595 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.X_SCM_FreqBandIndicatorConfigList",
596 is_invasive=False,
597 type=TrParameterType.STRING,
598 is_optional=False,
599 ),
600 ParameterName.EARFCNDL: TrParam(
601 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.EARFCNDL",
602 is_invasive=False,
603 type=TrParameterType.INT,
604 is_optional=False,
605 ),
606 ParameterName.EARFCNUL: TrParam(
607 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.EARFCNUL",
608 is_invasive=False,
609 type=TrParameterType.INT,
610 is_optional=False,
611 ),
612 ParameterName.EARFCNDL2: TrParam(
613 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.X_SCM_EARFCNDL2",
614 is_invasive=False,
615 type=TrParameterType.INT,
616 is_optional=False,
617 ),
618 ParameterName.EARFCNUL2: TrParam(
619 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.X_SCM_EARFCNUL2",
620 is_invasive=False,
621 type=TrParameterType.INT,
622 is_optional=False,
623 ),
624 ParameterName.EARFCNDL_LIST: TrParam(
625 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.X_SCM_EARFCNDLConfigList",
626 is_invasive=False,
627 type=TrParameterType.STRING,
628 is_optional=False,
629 ),
630 ParameterName.EARFCNUL_LIST: TrParam(
631 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.X_SCM_EARFCNULConfigList",
632 is_invasive=False,
633 type=TrParameterType.STRING,
634 is_optional=False,
635 ),
636 ParameterName.DL_BANDWIDTH: TrParam(
637 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.DLBandwidth",
638 is_invasive=False,
639 type=TrParameterType.STRING,
640 is_optional=False,
641 ),
642 ParameterName.UL_BANDWIDTH: TrParam(
643 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.ULBandwidth",
644 is_invasive=False,
645 type=TrParameterType.STRING,
646 is_optional=False,
647 ),
648 ParameterName.PCI: TrParam(
649 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.PhyCellID",
650 is_invasive=False,
651 type=TrParameterType.STRING,
652 is_optional=False,
653 ),
654 ParameterName.SUBFRAME_ASSIGNMENT: TrParam(
655 FAPSERVICE_PATH + "CellConfig.LTE.RAN.PHY.TDDFrame.SubFrameAssignment",
656 is_invasive=False,
657 type=TrParameterType.INT,
658 is_optional=False,
659 ),
660 ParameterName.SPECIAL_SUBFRAME_PATTERN: TrParam(
661 FAPSERVICE_PATH + "CellConfig.LTE.RAN.PHY.TDDFrame.SpecialSubframePatterns",
662 is_invasive=False,
663 type=TrParameterType.INT,
664 is_optional=False,
665 ),
666 ParameterName.CELL_ENABLE64QAM: TrParam(
667 FAPSERVICE_PATH + "CellConfig.LTE.RAN.PHY.PUSCH.Enable64QAM",
668 is_invasive=False,
669 type=TrParameterType.BOOLEAN,
670 is_optional=False,
671 ),
672 ParameterName.CELL_ID: TrParam(
673 FAPSERVICE_PATH + "CellConfig.LTE.RAN.Common.CellIdentity",
674 is_invasive=False,
675 type=TrParameterType.UNSIGNED_INT,
676 is_optional=False,
677 ),
678 # Readonly LTE state
679 ParameterName.ADMIN_STATE: TrParam(
680 FAP_CONTROL + "LTE.AdminState",
681 is_invasive=False,
682 type=TrParameterType.BOOLEAN,
683 is_optional=False,
684 ),
685 ParameterName.GPS_ENABLE: TrParam(
686 "Device.FAP.GPS.ScanOnBoot",
687 is_invasive=False,
688 type=TrParameterType.BOOLEAN,
689 is_optional=False,
690 ),
691 # Core network parameters
692 ParameterName.MME_IP: TrParam(
693 FAP_CONTROL + "LTE.Gateway.S1SigLinkServerList",
694 is_invasive=False,
695 type=TrParameterType.STRING,
696 is_optional=False,
697 ),
698 ParameterName.MME_PORT: TrParam(
699 FAP_CONTROL + "LTE.Gateway.S1SigLinkPort",
700 is_invasive=False,
701 type=TrParameterType.INT,
702 is_optional=False,
703 ),
704 ParameterName.TAC: TrParam(
705 FAPSERVICE_PATH + "CellConfig.LTE.EPC.TAC",
706 is_invasive=False,
707 type=TrParameterType.INT,
708 is_optional=False,
709 ),
710 ParameterName.TAC2: TrParam(
711 FAPSERVICE_PATH + "CellConfig.LTE.EPC.X_SCM_TAC2",
712 is_invasive=False,
713 type=TrParameterType.INT,
714 is_optional=False,
715 ),
716 # Management server parameters
717 ParameterName.PERIODIC_INFORM_ENABLE: TrParam(
718 "Device.ManagementServer.PeriodicInformEnable",
719 is_invasive=False,
720 type=TrParameterType.BOOLEAN,
721 is_optional=False,
722 ),
723 ParameterName.PERIODIC_INFORM_INTERVAL: TrParam(
724 "Device.ManagementServer.PeriodicInformInterval",
725 is_invasive=False,
726 type=TrParameterType.INT,
727 is_optional=False,
728 ),
729 # Performance management parameters
730 ParameterName.PERF_MGMT_ENABLE: TrParam(
731 "Device.FAP.PerfMgmt.Config.1.Enable",
732 is_invasive=False,
733 type=TrParameterType.BOOLEAN,
734 is_optional=False,
735 ),
736 ParameterName.PERF_MGMT_UPLOAD_INTERVAL: TrParam(
737 "Device.FAP.PerfMgmt.Config.1.PeriodicUploadInterval",
738 is_invasive=False,
739 type=TrParameterType.INT,
740 is_optional=False,
741 ),
742 ParameterName.PERF_MGMT_UPLOAD_URL: TrParam(
743 "Device.FAP.PerfMgmt.Config.1.URL",
744 is_invasive=False,
745 type=TrParameterType.STRING,
746 is_optional=False,
747 ),
748 ParameterName.TX_POWER: TrParam(
749 FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.X_SCM_TxPowerConfig",
750 is_invasive=True,
751 type=TrParameterType.INT,
752 is_optional=False,
753 ),
754 ParameterName.TUNNEL_TYPE: TrParam(
755 FAPSERVICE_PATH + "CellConfig.LTE.Tunnel.1.TunnelRef",
756 is_invasive=False,
757 type=TrParameterType.STRING,
758 is_optional=False,
759 ),
760 }
761 TRANSFORMS_FOR_ENB = {}
762 NUM_PLMNS_IN_CONFIG = 1
763 for i in range(1, NUM_PLMNS_IN_CONFIG + 1):
764 PARAMETERS[ParameterName.PLMN_N % i] = TrParam(
765 FAPSERVICE_PATH + "CellConfig.LTE.EPC.PLMNList.%d." % i,
766 is_invasive=False,
767 type=TrParameterType.STRING,
768 is_optional=False,
769 )
770 PARAMETERS[ParameterName.PLMN_N_CELL_RESERVED % i] = TrParam(
771 FAPSERVICE_PATH
772 + "CellConfig.LTE.EPC.PLMNList.%d.CellReservedForOperatorUse" % i,
773 is_invasive=False,
774 type=TrParameterType.BOOLEAN,
775 is_optional=False,
776 )
777 PARAMETERS[ParameterName.PLMN_N_ENABLE % i] = TrParam(
778 FAPSERVICE_PATH + "CellConfig.LTE.EPC.PLMNList.%d.Enable" % i,
779 is_invasive=False,
780 type=TrParameterType.BOOLEAN,
781 is_optional=False,
782 )
783 PARAMETERS[ParameterName.PLMN_N_PRIMARY % i] = TrParam(
784 FAPSERVICE_PATH + "CellConfig.LTE.EPC.PLMNList.%d.IsPrimary" % i,
785 is_invasive=False,
786 type=TrParameterType.BOOLEAN,
787 is_optional=False,
788 )
789 PARAMETERS[ParameterName.PLMN_N_PLMNID % i] = TrParam(
790 FAPSERVICE_PATH + "CellConfig.LTE.EPC.PLMNList.%d.PLMNID" % i,
791 is_invasive=False,
792 type=TrParameterType.STRING,
793 is_optional=False,
794 )
795
796 PARAMETERS.update(SASParameters.SAS_PARAMETERS)
797 PARAMETERS.update(SercommMiscParameters.MISC_PARAMETERS)
798 PARAMETERS.update(StatusParameters.STATUS_PARAMETERS)
799 PARAMETERS.update(StatusParameters.DERIVED_STATUS_PARAMETERS)
800
801 TRANSFORMS_FOR_MAGMA = {
802 # We don't set these parameters
803 ParameterName.BAND_CAPABILITY: transform_for_magma.band_capability,
804 ParameterName.DUPLEX_MODE_CAPABILITY: transform_for_magma.duplex_mode,
805 }
806
807 @classmethod
808 def get_parameter(cls, param_name: ParameterName) -> Optional[TrParam]:
809 return cls.PARAMETERS.get(param_name)
810
811 @classmethod
812 def _get_magma_transforms(cls,) -> Dict[ParameterName, Callable[[Any], Any]]:
813 return cls.TRANSFORMS_FOR_MAGMA
814
815 @classmethod
816 def _get_enb_transforms(cls) -> Dict[ParameterName, Callable[[Any], Any]]:
817 return cls.TRANSFORMS_FOR_ENB
818
819 @classmethod
820 def get_load_parameters(cls) -> List[ParameterName]:
821 """
822 Load all the parameters instead of a subset.
823 """
824 return list(cls.PARAMETERS.keys())
825
826 @classmethod
827 def get_num_plmns(cls) -> int:
828 return cls.NUM_PLMNS_IN_CONFIG
829
830 @classmethod
831 def get_parameter_names(cls) -> List[ParameterName]:
832 excluded_params = [
833 str(ParameterName.DEVICE),
834 str(ParameterName.FAP_SERVICE),
835 ]
836 names = list(
837 filter(
838 lambda x: (not str(x).startswith("PLMN"))
839 and (str(x) not in excluded_params),
840 cls.PARAMETERS.keys(),
841 ),
842 )
843 return names
844
845 @classmethod
846 def get_numbered_param_names(cls,) -> Dict[ParameterName, List[ParameterName]]:
847 names = {}
848 for i in range(1, cls.NUM_PLMNS_IN_CONFIG + 1):
849 params = [
850 ParameterName.PLMN_N_CELL_RESERVED % i,
851 ParameterName.PLMN_N_ENABLE % i,
852 ParameterName.PLMN_N_PRIMARY % i,
853 ParameterName.PLMN_N_PLMNID % i,
854 ]
855 names[ParameterName.PLMN_N % i] = params
856
857 return names
858
859 @classmethod
860 def get_sas_param_names(cls) -> List[ParameterName]:
861 return SASParameters.SAS_PARAMETERS.keys()
862
863
864class SercommConfigurationInitializer(EnodebConfigurationPostProcessor):
865 """
866 Class to add the sas related parameters to the desired config.
867 """
868
869 SAS_KEY = "sas"
870
871 def __init__(self, acs: EnodebAcsStateMachine):
872 super().__init__()
873 self.acs = acs
874
875 def postprocess(
876 self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration,
877 ) -> None:
878 # Bump up the parameter key version
879 self.acs.parameter_version_inc()
880
881 # Load eNB customized configuration from "./magma_config/serial_number/"
882 # and configure each connected eNB based on serial number
883 enbcfg = load_enb_config()
884 sn = self.acs.get_parameter(ParameterName.SERIAL_NUMBER)
885
886 for name, val in enbcfg.get(sn, {}).items():
887 if desired_cfg.has_parameter(name):
888 desired_cfg.set_parameter(name, val)
889 print("Config %s updated to: %s" % (name, val))
890
891 print(desired_cfg)
892
893
894class SercommSendGetTransientParametersState(EnodebAcsState):
895 """
896 Periodically read eNodeB status. Note: keep frequency low to avoid
897 backing up large numbers of read operations if enodebd is busy.
898 Some eNB parameters are read only and updated by the eNB itself.
899 """
900
901 def __init__(self, acs: EnodebAcsStateMachine, when_upgrade: str, when_done: str):
902 super().__init__()
903 self.acs = acs
904 self.upgrade_transition = when_upgrade
905 self.done_transition = when_done
906
907 def get_msg(self, message: Any) -> AcsMsgAndTransition:
908
909 request = models.GetParameterValues()
910 request.ParameterNames = models.ParameterNames()
911 request.ParameterNames.string = []
912
913 for _, tr_param in StatusParameters.STATUS_PARAMETERS.items():
914 path = tr_param.path
915 request.ParameterNames.string.append(path)
916
917 request.ParameterNames.string.append(
918 "Device.X_SCM_DeviceFeature.SystemParams.RunControl.Service.4.Status",
919 )
920
921 request.ParameterNames.arrayType = "xsd:string[%d]" % len(
922 request.ParameterNames.string
923 )
924
925 return AcsMsgAndTransition(msg=request, next_state=None)
926
927 def read_msg(self, message: Any) -> AcsReadMsgResult:
928
929 name_to_val = parse_get_parameter_values_response(self.acs.data_model, message,)
930
931 # Update device configuration
932 StatusParameters.set_magma_device_cfg(
933 name_to_val, self.acs.device_cfg,
934 )
935
936 if name_to_val["SW version"] != "RC3923@2202151120":
937 print("Get into Firmware Upgrade state")
938 return AcsReadMsgResult(
939 msg_handled=True, next_state=self.upgrade_transition
940 )
941
942 print("Skip firmware upgrade, configure the enb")
943 return AcsReadMsgResult(msg_handled=True, next_state=self.done_transition)
944
945 def state_description(self) -> str:
946 return "Getting transient read-only parameters"
947
948
949class SercommGetInitState(EnodebAcsState):
950 """
951 After the first Inform message the following can happen:
952 1 - eNB can try to learn the RPC method of the ACS, reply back with the
953 RPC response (happens right after boot)
954 2 - eNB can send an empty message -> This means that the eNB is already
955 provisioned so transition to next state. Only transition to next state
956 after this message.
957 3 - Some other method call that we don't care about so ignore.
958 expected that the eNB -> This is an unhandled state so unlikely
959 """
960
961 def __init__(self, acs: EnodebAcsStateMachine, when_done):
962 super().__init__()
963 self.acs = acs
964 self.done_transition = when_done
965 self._is_rpc_request = False
966
967 def get_msg(self, message: Any) -> AcsMsgAndTransition:
968 """
969 Return empty message response if care about this
970 message type otherwise return empty RPC methods response.
971 """
972 if not self._is_rpc_request:
973 resp = models.DummyInput()
974 return AcsMsgAndTransition(msg=resp, next_state=None)
975
976 resp = models.GetRPCMethodsResponse()
977 resp.MethodList = models.MethodList()
978 RPC_METHODS = ["Inform", "GetRPCMethods", "TransferComplete"]
979 resp.MethodList.arrayType = "xsd:string[%d]" % len(RPC_METHODS)
980 resp.MethodList.string = RPC_METHODS
981 # Don't transition to next state wait for the empty HTTP post
982 return AcsMsgAndTransition(msg=resp, next_state=None)
983
984 def read_msg(self, message: Any) -> AcsReadMsgResult:
985 # If this is a regular Inform, not after a reboot we'll get an empty
986 # message, in this case transition to the next state. We consider
987 # this phase as "initialized"
988 if isinstance(message, models.DummyInput):
989 return AcsReadMsgResult(msg_handled=True, next_state=self.done_transition,)
990 if not isinstance(message, models.GetRPCMethods):
991 # Unexpected, just don't die, ignore message.
992 logging.error("Ignoring message %s", str(type(message)))
993 # Set this so get_msg will return an empty message
994 self._is_rpc_request = False
995 else:
996 # Return a valid RPC response
997 self._is_rpc_request = True
998 return AcsReadMsgResult(msg_handled=True, next_state=None)
999
1000 def state_description(self) -> str:
1001 return "Initializing the post boot sequence for eNB"
1002
1003
1004class SercommGetObjectParametersState(EnodebAcsState):
1005 """
1006 Get information on parameters belonging to objects that can be added or
1007 removed from the configuration.
1008
1009 Englewood will report a parameter value as None if it does not exist
1010 in the data model, rather than replying with a Fault message like most
1011 eNB devices.
1012 """
1013
1014 def __init__(
1015 self,
1016 acs: EnodebAcsStateMachine,
1017 when_delete: str,
1018 when_add: str,
1019 when_set: str,
1020 when_skip: str,
1021 ):
1022 super().__init__()
1023 self.acs = acs
1024 self.rm_obj_transition = when_delete
1025 self.add_obj_transition = when_add
1026 self.set_params_transition = when_set
1027 self.skip_transition = when_skip
1028
1029 def get_params_to_get(self, data_model: DataModel,) -> List[ParameterName]:
1030 names = []
1031
1032 # First get base params
1033 names = get_params_to_get(
1034 self.acs.device_cfg, self.acs.data_model, request_all_params=True,
1035 )
1036 # Add object params.
1037 num_plmns = data_model.get_num_plmns()
1038 obj_to_params = data_model.get_numbered_param_names()
1039 for i in range(1, num_plmns + 1):
1040 obj_name = ParameterName.PLMN_N % i
1041 desired = obj_to_params[obj_name]
1042 names += desired
1043
1044 print(obj_to_params)
1045 return names
1046
1047 def get_msg(self, message: Any) -> AcsMsgAndTransition:
1048 """ Respond with GetParameterValuesRequest """
1049 names = self.get_params_to_get(self.acs.data_model,)
1050
1051 # Generate the request
1052 request = models.GetParameterValues()
1053 request.ParameterNames = models.ParameterNames()
1054 request.ParameterNames.arrayType = "xsd:string[%d]" % len(names)
1055 request.ParameterNames.string = []
1056 for name in names:
1057 path = self.acs.data_model.get_parameter(name).path
1058 if path is not InvalidTrParamPath:
1059 # Only get data elements backed by tr69 path
1060 request.ParameterNames.string.append(path)
1061
1062 return AcsMsgAndTransition(msg=request, next_state=None)
1063
1064 def read_msg(self, message: Any) -> AcsReadMsgResult:
1065 """
1066 Process GetParameterValuesResponse
1067 """
1068 if not isinstance(message, models.GetParameterValuesResponse):
1069 return AcsReadMsgResult(msg_handled=False, next_state=None)
1070
1071 path_to_val = {}
1072 for param_value_struct in message.ParameterList.ParameterValueStruct:
1073 path_to_val[param_value_struct.Name] = param_value_struct.Value.Data
1074
1075 EnodebdLogger.debug("Received object parameters: %s", str(path_to_val))
1076
1077 # Parse simple params
1078 param_name_list = self.acs.data_model.get_parameter_names()
1079
1080 for name in param_name_list:
1081 path = self.acs.data_model.get_parameter(name).path
1082 if path in path_to_val:
1083 value = path_to_val.get(path)
1084 magma_val = self.acs.data_model.transform_for_magma(name, value,)
1085 self.acs.device_cfg.set_parameter(name, magma_val)
1086
1087 # Parse object params
1088 num_plmns = self.acs.data_model.get_num_plmns()
1089 for i in range(1, num_plmns + 1):
1090 obj_name = ParameterName.PLMN_N % i
1091 obj_to_params = self.acs.data_model.get_numbered_param_names()
1092 param_name_list = obj_to_params[obj_name]
1093 for name in param_name_list:
1094 path = self.acs.data_model.get_parameter(name).path
1095 if path in path_to_val:
1096 value = path_to_val.get(path)
1097 if value is None:
1098 continue
1099 if obj_name not in self.acs.device_cfg.get_object_names():
1100 self.acs.device_cfg.add_object(obj_name)
1101 magma_value = self.acs.data_model.transform_for_magma(name, value)
1102 self.acs.device_cfg.set_parameter_for_object(
1103 name, magma_value, obj_name,
1104 )
1105
1106 # Now we have enough information to build the desired configuration
1107 if self.acs.desired_cfg is None:
1108 self.acs.desired_cfg = build_desired_config(
1109 self.acs.mconfig,
1110 self.acs.service_config,
1111 self.acs.device_cfg,
1112 self.acs.data_model,
1113 self.acs.config_postprocessor,
1114 )
1115
1116 if (
1117 len(get_all_objects_to_delete(self.acs.desired_cfg, self.acs.device_cfg,),)
1118 > 0
1119 ):
1120 return AcsReadMsgResult(
1121 msg_handled=True, next_state=self.rm_obj_transition,
1122 )
1123 elif (
1124 len(get_all_objects_to_add(self.acs.desired_cfg, self.acs.device_cfg,),) > 0
1125 ):
1126 return AcsReadMsgResult(
1127 msg_handled=True, next_state=self.add_obj_transition,
1128 )
1129 elif (
1130 len(
1131 get_all_param_values_to_set(
1132 self.acs.desired_cfg, self.acs.device_cfg, self.acs.data_model,
1133 ),
1134 )
1135 > 0
1136 ):
1137 return AcsReadMsgResult(
1138 msg_handled=True, next_state=self.set_params_transition,
1139 )
1140 return AcsReadMsgResult(msg_handled=True, next_state=self.skip_transition,)
1141
1142 def state_description(self) -> str:
1143 return "Getting well known parameters"