blob: c62a622b3062eaa7e313be11caffd41eeed46459 [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
14from typing import Any, Callable, Dict, List, Optional, Type
15
16from common.service import MagmaService
17from data_models import transform_for_enb, transform_for_magma
18from data_models.data_model import DataModel, TrParam
19from data_models.data_model_parameters import (
20 ParameterName,
21 TrParameterType,
22)
23from device_config.enodeb_config_postprocessor import (
24 EnodebConfigurationPostProcessor,
25)
26from device_config.enodeb_configuration import EnodebConfiguration
27from devices.device_utils import EnodebDeviceName
28from exceptions import Tr069Error
29from logger import EnodebdLogger as logger
30from state_machines.acs_state_utils import (
31 get_all_objects_to_add,
32 get_all_objects_to_delete,
33)
34from state_machines.enb_acs import EnodebAcsStateMachine
35from state_machines.enb_acs_impl import BasicEnodebAcsStateMachine
36from state_machines.enb_acs_states import (
37 AcsMsgAndTransition,
38 AcsReadMsgResult,
39 AddObjectsState,
40 DeleteObjectsState,
41 EndSessionState,
42 EnodebAcsState,
43 ErrorState,
44 GetParametersState,
45 GetRPCMethodsState,
46 SendGetTransientParametersState,
47 SendRebootState,
48 SetParameterValuesNotAdminState,
49 WaitEmptyMessageState,
50 WaitGetObjectParametersState,
51 WaitGetParametersState,
52 WaitGetTransientParametersState,
53 WaitInformMRebootState,
54 WaitInformState,
55 WaitRebootResponseState,
56 WaitSetParameterValuesState,
57)
58from tr069 import models
59
60
61class CaviumHandler(BasicEnodebAcsStateMachine):
62 def __init__(
63 self,
64 service: MagmaService,
65 ) -> None:
66 self._state_map = {}
67 super().__init__(service=service, use_param_key=False)
68
69 def reboot_asap(self) -> None:
70 self.transition('reboot')
71
72 def is_enodeb_connected(self) -> bool:
73 return not isinstance(self.state, WaitInformState)
74
75 def _init_state_map(self) -> None:
76 self._state_map = {
77 'wait_inform': WaitInformState(self, when_done='get_rpc_methods'),
78 'get_rpc_methods': GetRPCMethodsState(self, when_done='wait_empty', when_skip='get_transient_params'),
79 'wait_empty': WaitEmptyMessageState(self, when_done='get_transient_params'),
80 'get_transient_params': SendGetTransientParametersState(self, when_done='wait_get_transient_params'),
81 'wait_get_transient_params': WaitGetTransientParametersState(self, when_get='get_params', when_get_obj_params='get_obj_params', when_delete='delete_objs', when_add='add_objs', when_set='set_params', when_skip='end_session'),
82 'get_params': GetParametersState(self, when_done='wait_get_params'),
83 'wait_get_params': WaitGetParametersState(self, when_done='get_obj_params'),
84 'get_obj_params': CaviumGetObjectParametersState(self, when_done='wait_get_obj_params'),
85 'wait_get_obj_params': CaviumWaitGetObjectParametersState(self, when_edit='disable_admin', when_skip='get_transient_params'),
86 'disable_admin': CaviumDisableAdminEnableState(self, admin_value=False, when_done='wait_disable_admin'),
87 'wait_disable_admin': CaviumWaitDisableAdminEnableState(self, admin_value=False, when_add='add_objs', when_delete='delete_objs', when_done='set_params'),
88 'delete_objs': DeleteObjectsState(self, when_add='add_objs', when_skip='set_params'),
89 'add_objs': AddObjectsState(self, when_done='set_params'),
90 'set_params': SetParameterValuesNotAdminState(self, when_done='wait_set_params'),
91 'wait_set_params': WaitSetParameterValuesState(self, when_done='enable_admin', when_apply_invasive='enable_admin'),
92 'enable_admin': CaviumDisableAdminEnableState(self, admin_value=True, when_done='wait_enable_admin'),
93 'wait_enable_admin': CaviumWaitDisableAdminEnableState(self, admin_value=True, when_done='check_get_params', when_add='check_get_params', when_delete='check_get_params'),
94 'check_get_params': GetParametersState(self, when_done='check_wait_get_params', request_all_params=True),
95 'check_wait_get_params': WaitGetParametersState(self, when_done='end_session'),
96 'end_session': EndSessionState(self),
97 # Below states only entered through manual user intervention
98 'reboot': SendRebootState(self, when_done='wait_reboot'),
99 'wait_reboot': WaitRebootResponseState(self, when_done='wait_post_reboot_inform'),
100 'wait_post_reboot_inform': WaitInformMRebootState(self, when_done='wait_reboot_delay', when_timeout='wait_inform'),
101 # The states below are entered when an unexpected message type is
102 # received
103 'unexpected_fault': ErrorState(self, inform_transition_target='wait_inform'),
104 }
105
106 @property
107 def device_name(self) -> str:
108 return EnodebDeviceName.CAVIUM
109
110 @property
111 def data_model_class(self) -> Type[DataModel]:
112 return CaviumTrDataModel
113
114 @property
115 def config_postprocessor(self) -> EnodebConfigurationPostProcessor:
116 return CaviumTrConfigurationInitializer()
117
118 @property
119 def state_map(self) -> Dict[str, EnodebAcsState]:
120 return self._state_map
121
122 @property
123 def disconnected_state_name(self) -> str:
124 return 'wait_inform'
125
126 @property
127 def unexpected_fault_state_name(self) -> str:
128 return 'unexpected_fault'
129
130
131class CaviumGetObjectParametersState(EnodebAcsState):
132 """
133 When booted, the PLMN list is empty so we cannot get individual
134 object parameters. Instead, get the parent object PLMN_LIST
135 which will include any children if they exist.
136 """
137
138 def __init__(self, acs: EnodebAcsStateMachine, when_done: str):
139 super().__init__()
140 self.acs = acs
141 self.done_transition = when_done
142
143 def get_msg(self, message: Any) -> AcsMsgAndTransition:
144 """ Respond with GetParameterValuesRequest """
145 names = [ParameterName.PLMN_LIST]
146
147 # Generate the request
148 request = models.GetParameterValues()
149 request.ParameterNames = models.ParameterNames()
150 request.ParameterNames.arrayType = 'xsd:string[%d]' \
151 % len(names)
152 request.ParameterNames.string = []
153 for name in names:
154 path = self.acs.data_model.get_parameter(name).path
155 request.ParameterNames.string.append(path)
156
157 return AcsMsgAndTransition(request, self.done_transition)
158
159 def state_description(self) -> str:
160 return 'Getting object parameters'
161
162
163class CaviumWaitGetObjectParametersState(WaitGetObjectParametersState):
164 def __init__(
165 self,
166 acs: EnodebAcsStateMachine,
167 when_edit: str,
168 when_skip: str,
169 ):
170 super().__init__(
171 acs=acs,
172 when_add=when_edit,
173 when_delete=when_edit,
174 when_set=when_edit,
175 when_skip=when_skip,
176 )
177
178
179class CaviumDisableAdminEnableState(EnodebAcsState):
180 """
181 Cavium requires that we disable 'Admin Enable' before configuring
182 most parameters
183 """
184
185 def __init__(self, acs: EnodebAcsStateMachine, admin_value: bool, when_done: str):
186 super().__init__()
187 self.acs = acs
188 self.admin_value = admin_value
189 self.done_transition = when_done
190
191 def read_msg(self, message: Any) -> AcsReadMsgResult:
192 if not isinstance(message, models.DummyInput):
193 return AcsReadMsgResult(False, None)
194 return AcsReadMsgResult(True, None)
195
196 def get_msg(self, message: Any) -> AcsMsgAndTransition:
197 """
198 Returns:
199 A SetParameterValueRequest for setting 'Admin Enable' to False
200 """
201 param_name = ParameterName.ADMIN_STATE
202 # if we want the cell to be down don't force it up
203 desired_admin_value = \
204 self.acs.desired_cfg.get_parameter(param_name) \
205 and self.admin_value
206 admin_value = \
207 self.acs.data_model.transform_for_enb(
208 param_name,
209 desired_admin_value,
210 )
211 admin_path = self.acs.data_model.get_parameter(param_name).path
212 param_values = {admin_path: admin_value}
213
214 request = models.SetParameterValues()
215 request.ParameterList = models.ParameterValueList()
216 request.ParameterList.arrayType = 'cwmp:ParameterValueStruct[%d]' \
217 % len(param_values)
218
219 name_value = models.ParameterValueStruct()
220 name_value.Name = admin_path
221 name_value.Value = models.anySimpleType()
222 name_value.Value.type = 'xsd:string'
223 name_value.Value.Data = str(admin_value)
224 request.ParameterList.ParameterValueStruct = [name_value]
225
226 return AcsMsgAndTransition(request, self.done_transition)
227
228 def state_description(self) -> str:
229 return 'Disabling admin_enable (Cavium only)'
230
231
232class CaviumWaitDisableAdminEnableState(EnodebAcsState):
233 def __init__(
234 self,
235 acs: EnodebAcsStateMachine,
236 admin_value: bool,
237 when_done: str,
238 when_add: str,
239 when_delete: str,
240 ):
241 super().__init__()
242 self.acs = acs
243 self.done_transition = when_done
244 self.add_obj_transition = when_add
245 self.del_obj_transition = when_delete
246 self.admin_value = admin_value
247
248 def read_msg(self, message: Any) -> Optional[str]:
249 if type(message) == models.Fault:
250 logger.error('Received Fault in response to SetParameterValues')
251 if message.SetParameterValuesFault is not None:
252 for fault in message.SetParameterValuesFault:
253 logger.error(
254 'SetParameterValuesFault Param: %s, Code: %s, String: %s',
255 fault.ParameterName, fault.FaultCode, fault.FaultString,
256 )
257 raise Tr069Error(
258 'Received Fault in response to SetParameterValues '
259 '(faultstring = %s)' % message.FaultString,
260 )
261 elif not isinstance(message, models.SetParameterValuesResponse):
262 return AcsReadMsgResult(False, None)
263 if message.Status != 0:
264 raise Tr069Error(
265 'Received SetParameterValuesResponse with '
266 'Status=%d' % message.Status,
267 )
268 param_name = ParameterName.ADMIN_STATE
269 desired_admin_value = \
270 self.acs.desired_cfg.get_parameter(param_name) \
271 and self.admin_value
272 magma_value = \
273 self.acs.data_model.transform_for_magma(
274 param_name,
275 desired_admin_value,
276 )
277 self.acs.device_cfg.set_parameter(param_name, magma_value)
278
279 if len(
280 get_all_objects_to_delete(
281 self.acs.desired_cfg,
282 self.acs.device_cfg,
283 ),
284 ) > 0:
285 return AcsReadMsgResult(True, self.del_obj_transition)
286 elif len(
287 get_all_objects_to_add(
288 self.acs.desired_cfg,
289 self.acs.device_cfg,
290 ),
291 ) > 0:
292 return AcsReadMsgResult(True, self.add_obj_transition)
293 else:
294 return AcsReadMsgResult(True, self.done_transition)
295
296 def state_description(self) -> str:
297 return 'Disabling admin_enable (Cavium only)'
298
299
300class CaviumTrDataModel(DataModel):
301 """
302 Class to represent relevant data model parameters from TR-196/TR-098/TR-181.
303 This class is effectively read-only
304 """
305 # Mapping of TR parameter paths to aliases
306 DEVICE_PATH = 'Device.'
307 FAPSERVICE_PATH = DEVICE_PATH + 'Services.FAPService.1.'
308 PARAMETERS = {
309 # Top-level objects
310 ParameterName.DEVICE: TrParam(DEVICE_PATH, True, TrParameterType.OBJECT, False),
311 ParameterName.FAP_SERVICE: TrParam(FAPSERVICE_PATH, True, TrParameterType.OBJECT, False),
312
313 # Device info parameters
314 ParameterName.GPS_STATUS: TrParam(DEVICE_PATH + 'FAP.GPS.ContinuousGPSStatus.GotFix', True, TrParameterType.BOOLEAN, False),
315 ParameterName.GPS_LAT: TrParam(DEVICE_PATH + 'FAP.GPS.LockedLatitude', True, TrParameterType.INT, False),
316 ParameterName.GPS_LONG: TrParam(DEVICE_PATH + 'FAP.GPS.LockedLongitude', True, TrParameterType.INT, False),
317 ParameterName.SW_VERSION: TrParam(DEVICE_PATH + 'DeviceInfo.SoftwareVersion', True, TrParameterType.STRING, False),
318 ParameterName.SERIAL_NUMBER: TrParam(DEVICE_PATH + 'DeviceInfo.SerialNumber', True, TrParameterType.STRING, False),
319
320 # Capabilities
321 ParameterName.DUPLEX_MODE_CAPABILITY: TrParam(
322 FAPSERVICE_PATH + 'Capabilities.LTE.DuplexMode', True, TrParameterType.STRING, False,
323 ),
324 ParameterName.BAND_CAPABILITY: TrParam(FAPSERVICE_PATH + 'Capabilities.LTE.BandsSupported', True, TrParameterType.UNSIGNED_INT, False),
325
326 # RF-related parameters
327 ParameterName.EARFCNDL: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.EARFCNDL', True, TrParameterType.UNSIGNED_INT, False),
328 ParameterName.EARFCNUL: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.EARFCNUL', True, TrParameterType.UNSIGNED_INT, False),
329 ParameterName.BAND: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.FreqBandIndicator', True, TrParameterType.UNSIGNED_INT, False),
330 ParameterName.PCI: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', True, TrParameterType.STRING, False),
331 ParameterName.DL_BANDWIDTH: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.DLBandwidth', True, TrParameterType.STRING, False),
332 ParameterName.UL_BANDWIDTH: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.ULBandwidth', True, TrParameterType.STRING, False),
333 ParameterName.CELL_ID: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.Common.CellIdentity', True, TrParameterType.UNSIGNED_INT, False),
334
335 # Other LTE parameters
336 ParameterName.ADMIN_STATE: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.AdminState', False, TrParameterType.BOOLEAN, False),
337 ParameterName.OP_STATE: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.OpState', True, TrParameterType.BOOLEAN, False),
338 ParameterName.RF_TX_STATUS: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.RFTxStatus', True, TrParameterType.BOOLEAN, False),
339
340 # RAN parameters
341 ParameterName.CELL_RESERVED: TrParam(
342 FAPSERVICE_PATH
343 + 'CellConfig.LTE.RAN.CellRestriction.CellReservedForOperatorUse', True, TrParameterType.BOOLEAN, False,
344 ),
345 ParameterName.CELL_BARRED: TrParam(
346 FAPSERVICE_PATH
347 + 'CellConfig.LTE.RAN.CellRestriction.CellBarred', True, TrParameterType.BOOLEAN, False,
348 ),
349
350 # Core network parameters
351 ParameterName.MME_IP: TrParam(
352 FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkServerList', True, TrParameterType.STRING, False,
353 ),
354 ParameterName.MME_PORT: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkPort', True, TrParameterType.UNSIGNED_INT, False),
355 ParameterName.NUM_PLMNS: TrParam(
356 FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNListNumberOfEntries', True, TrParameterType.UNSIGNED_INT, False,
357 ),
358 ParameterName.PLMN: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNList.', True, TrParameterType.OBJECT, False),
359 # PLMN arrays are added below
360 ParameterName.TAC: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.UNSIGNED_INT, False),
361 ParameterName.IP_SEC_ENABLE: TrParam(
362 DEVICE_PATH + 'IPsec.Enable', False, TrParameterType.BOOLEAN, False,
363 ),
364 ParameterName.PERIODIC_INFORM_INTERVAL:
365 TrParam(DEVICE_PATH + 'ManagementServer.PeriodicInformInterval', False, TrParameterType.UNSIGNED_INT, False),
366
367 # Management server parameters
368 ParameterName.PERIODIC_INFORM_ENABLE: TrParam(
369 DEVICE_PATH + 'ManagementServer.PeriodicInformEnable',
370 False, TrParameterType.BOOLEAN, False,
371 ),
372 ParameterName.PERIODIC_INFORM_INTERVAL: TrParam(
373 DEVICE_PATH + 'ManagementServer.PeriodicInformInterval',
374 False, TrParameterType.UNSIGNED_INT, False,
375 ),
376
377 # Performance management parameters
378 ParameterName.PERF_MGMT_ENABLE: TrParam(
379 FAPSERVICE_PATH + 'PerfMgmt.Config.1.Enable', False, TrParameterType.BOOLEAN, False,
380 ),
381 ParameterName.PERF_MGMT_UPLOAD_INTERVAL: TrParam(
382 FAPSERVICE_PATH + 'PerfMgmt.Config.1.PeriodicUploadInterval', False, TrParameterType.UNSIGNED_INT, False,
383 ),
384 ParameterName.PERF_MGMT_UPLOAD_URL: TrParam(
385 FAPSERVICE_PATH + 'PerfMgmt.Config.1.URL', False, TrParameterType.STRING, False,
386 ),
387 ParameterName.PERF_MGMT_USER: TrParam(
388 FAPSERVICE_PATH + 'PerfMgmt.Config.1.Username',
389 False, TrParameterType.STRING, False,
390 ),
391 ParameterName.PERF_MGMT_PASSWORD: TrParam(
392 FAPSERVICE_PATH + 'PerfMgmt.Config.1.Password',
393 False, TrParameterType.STRING, False,
394 ),
395
396 # PLMN Info
397 ParameterName.PLMN_LIST: TrParam(
398 FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNList.', False, TrParameterType.OBJECT, False,
399 ),
400 }
401
402 NUM_PLMNS_IN_CONFIG = 6
403 for i in range(1, NUM_PLMNS_IN_CONFIG + 1):
404 PARAMETERS[ParameterName.PLMN_N % i] = TrParam(
405 FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNList.%d.' % i, True, TrParameterType.OBJECT, False,
406 )
407 PARAMETERS[ParameterName.PLMN_N_CELL_RESERVED % i] = TrParam(
408 FAPSERVICE_PATH
409 + 'CellConfig.LTE.EPC.PLMNList.%d.CellReservedForOperatorUse' % i, True, TrParameterType.BOOLEAN, False,
410 )
411 PARAMETERS[ParameterName.PLMN_N_ENABLE % i] = TrParam(
412 FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNList.%d.Enable' % i, True, TrParameterType.BOOLEAN, False,
413 )
414 PARAMETERS[ParameterName.PLMN_N_PRIMARY % i] = TrParam(
415 FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNList.%d.IsPrimary' % i, True, TrParameterType.BOOLEAN, False,
416 )
417 PARAMETERS[ParameterName.PLMN_N_PLMNID % i] = TrParam(
418 FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNList.%d.PLMNID' % i, True, TrParameterType.STRING, False,
419 )
420
421 TRANSFORMS_FOR_ENB = {
422 ParameterName.DL_BANDWIDTH: transform_for_enb.bandwidth,
423 ParameterName.UL_BANDWIDTH: transform_for_enb.bandwidth,
424 }
425 TRANSFORMS_FOR_MAGMA = {
426 ParameterName.DL_BANDWIDTH: transform_for_magma.bandwidth,
427 ParameterName.UL_BANDWIDTH: transform_for_magma.bandwidth,
428 # We don't set GPS, so we don't need transform for enb
429 ParameterName.GPS_LAT: transform_for_magma.gps_tr181,
430 ParameterName.GPS_LONG: transform_for_magma.gps_tr181,
431 }
432
433 @classmethod
434 def get_parameter(cls, param_name: ParameterName) -> Optional[TrParam]:
435 return cls.PARAMETERS.get(param_name)
436
437 @classmethod
438 def _get_magma_transforms(
439 cls,
440 ) -> Dict[ParameterName, Callable[[Any], Any]]:
441 return cls.TRANSFORMS_FOR_MAGMA
442
443 @classmethod
444 def _get_enb_transforms(cls) -> Dict[ParameterName, Callable[[Any], Any]]:
445 return cls.TRANSFORMS_FOR_ENB
446
447 @classmethod
448 def get_load_parameters(cls) -> List[ParameterName]:
449 """
450 Load all the parameters instead of a subset.
451 """
452 return [ParameterName.DEVICE]
453
454 @classmethod
455 def get_num_plmns(cls) -> int:
456 return cls.NUM_PLMNS_IN_CONFIG
457
458 @classmethod
459 def get_parameter_names(cls) -> List[ParameterName]:
460 excluded_params = [
461 str(ParameterName.DEVICE),
462 str(ParameterName.FAP_SERVICE),
463 ]
464 names = list(
465 filter(
466 lambda x: (not str(x).startswith('PLMN'))
467 and (str(x) not in excluded_params),
468 cls.PARAMETERS.keys(),
469 ),
470 )
471 return names
472
473 @classmethod
474 def get_numbered_param_names(
475 cls,
476 ) -> Dict[ParameterName, List[ParameterName]]:
477 names = {}
478 for i in range(1, cls.NUM_PLMNS_IN_CONFIG + 1):
479 params = []
480 params.append(ParameterName.PLMN_N_CELL_RESERVED % i)
481 params.append(ParameterName.PLMN_N_ENABLE % i)
482 params.append(ParameterName.PLMN_N_PRIMARY % i)
483 params.append(ParameterName.PLMN_N_PLMNID % i)
484 names[ParameterName.PLMN_N % i] = params
485 return names
486
487
488class CaviumTrConfigurationInitializer(EnodebConfigurationPostProcessor):
489 def postprocess(self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration) -> None:
490 desired_cfg.set_parameter(ParameterName.CELL_BARRED, True)
491 desired_cfg.set_parameter(ParameterName.ADMIN_STATE, True)