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