blob: 0c8ba271d15ed2cac81bed9ff1c9d02263bd78f4 [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 abc import ABC, abstractmethod
15from collections import namedtuple
16from typing import Any, Callable, Dict, List, Optional
17
18from data_models.data_model_parameters import ParameterName
19
20TrParam = namedtuple('TrParam', ['path', 'is_invasive', 'type', 'is_optional'])
21
22# We may want to model nodes in the datamodel that are derived from other fields
23# in the datamodel and thus maynot have a representation in tr69.
24# e.g PTP_STATUS in FreedomFiOne is True iff GPS is in sync and SyncStatus is
25# True.
26# Explicitly map these params to invalid paths so setters and getters know they
27# should not try to read or write these nodes on the eNB side.
28InvalidTrParamPath = "INVALID_TR_PATH"
29
30
31class DataModel(ABC):
32 """
33 Class to represent relevant data model parameters.
34
35 Also should contain transform functions for certain parameters that are
36 represented differently in the eNodeB device than it is in Magma.
37
38 Subclass this for each data model implementation.
39
40 This class is effectively read-only.
41 """
42
43 def __init__(self):
44 self._presence_by_param = {}
45
46 def are_param_presences_known(self) -> bool:
47 """
48 True if all optional parameters' presence are known in data model
49 """
50 optional_params = self.get_names_of_optional_params()
51 for param in optional_params:
52 if param not in self._presence_by_param:
53 return False
54 return True
55
56 def is_parameter_present(self, param_name: ParameterName) -> bool:
57 """ Is the parameter missing from the device's data model """
58 param_info = self.get_parameter(param_name)
59 if param_info is None:
60 return False
61 if not param_info.is_optional:
62 return True
63 if param_name not in self._presence_by_param:
64 raise KeyError(
65 'Parameter presence not yet marked in data '
66 'model: %s' % param_name,
67 )
68 return self._presence_by_param[param_name]
69
70 def set_parameter_presence(
71 self,
72 param_name: ParameterName,
73 is_present: bool,
74 ) -> None:
75 """ Mark optional parameter as either missing or not """
76 self._presence_by_param[param_name] = is_present
77
78 def get_missing_params(self) -> List[ParameterName]:
79 """
80 Return optional params confirmed to be missing from data model.
81 NOTE: Make sure we already know which parameters are present or not
82 """
83 all_missing = []
84 for param in self.get_names_of_optional_params():
85 if self.is_parameter_present(param):
86 all_missing.append(param)
87 return all_missing
88
89 def get_present_params(self) -> List[ParameterName]:
90 """
91 Return optional params confirmed to be present in data model.
92 NOTE: Make sure we already know which parameters are present or not
93 """
94 all_optional = self.get_names_of_optional_params()
95 all_present = self.get_parameter_names()
96 for param in all_optional:
97 if not self.is_parameter_present(param):
98 all_present.remove(param)
99 return all_present
100
101 @classmethod
102 def get_names_of_optional_params(cls) -> List[ParameterName]:
103 all_optional_params = []
104 for name in cls.get_parameter_names():
105 if cls.get_parameter(name).is_optional:
106 all_optional_params.append(name)
107 return all_optional_params
108
109 @classmethod
110 def transform_for_magma(
111 cls,
112 param_name: ParameterName,
113 enb_value: Any,
114 ) -> Any:
115 """
116 Convert a parameter from its device specific formatting to the
117 consistent format that magma understands.
118 For the same parameter, different data models have their own
119 idiosyncrasies. For this reason, it's important to nominalize these
120 values before processing them in Magma code.
121
122 Args:
123 param_name: The parameter name
124 enb_value: Native value of the parameter
125
126 Returns:
127 Returns the nominal value of the parameter that is understood
128 by Magma code.
129 """
130 transforms = cls._get_magma_transforms()
131 if param_name in transforms:
132 transform_function = transforms[param_name]
133 return transform_function(enb_value)
134 return enb_value
135
136 @classmethod
137 def transform_for_enb(
138 cls,
139 param_name: ParameterName,
140 magma_value: Any,
141 ) -> Any:
142 """
143 Convert a parameter from the format that Magma understands to
144 the device specific formatting.
145 For the same parameter, different data models have their own
146 idiosyncrasies. For this reason, it's important to nominalize these
147 values before processing them in Magma code.
148
149 Args:
150 param_name: The parameter name. The transform is dependent on the
151 exact parameter.
152 magma_value: Nominal value of the parameter.
153
154 Returns:
155 Returns the native value of the parameter that will be set in the
156 CPE data model configuration.
157 """
158 transforms = cls._get_enb_transforms()
159 if param_name in transforms:
160 transform_function = transforms[param_name]
161 return transform_function(magma_value)
162 return magma_value
163
164 @classmethod
165 def get_parameter_name_from_path(
166 cls,
167 param_path: str,
168 ) -> Optional[ParameterName]:
169 """
170 Args:
171 param_path: Parameter path,
172 eg. "Device.DeviceInfo.X_BAICELLS_COM_GPS_Status"
173 Returns:
174 ParameterName or None if there is no ParameterName matching
175 """
176 all_param_names = cls.get_parameter_names()
177 numbered_param_names = cls.get_numbered_param_names()
178 for _obj_name, param_name_list in numbered_param_names.items():
179 all_param_names = all_param_names + param_name_list
180
181 for param_name in all_param_names:
182 param_info = cls.get_parameter(param_name)
183 if param_info is not None and param_path == param_info.path:
184 return param_name
185 return None
186
187 @classmethod
188 @abstractmethod
189 def get_parameter(cls, param_name: ParameterName) -> Optional[TrParam]:
190 """
191 Args:
192 param_name: String of the parameter name
193
194 Returns:
195 TrParam or None if it doesn't exist
196 """
197 pass
198
199 @classmethod
200 @abstractmethod
201 def _get_magma_transforms(
202 cls,
203 ) -> Dict[ParameterName, Callable[[Any], Any]]:
204 """
205 For the same parameter, different data models have their own
206 idiosyncrasies. For this reason, it's important to nominalize these
207 values before processing them in Magma code.
208
209 Returns:
210 Dictionary with key of parameter name, and value of a transform
211 function taking the device-specific value of the parameter and
212 returning the value in format understood by Magma.
213 """
214 pass
215
216 @classmethod
217 @abstractmethod
218 def _get_enb_transforms(
219 cls,
220 ) -> Dict[ParameterName, Callable[[Any], Any]]:
221 """
222 For the same parameter, different data models have their own
223 idiosyncrasies. For this reason, it's important to nominalize these
224 values before processing them in Magma code.
225
226 Returns:
227 Dictionary with key of parameter name, and value of a transform
228 function taking the nominal value of the parameter and returning
229 the device-understood value.
230 """
231 pass
232
233 @classmethod
234 @abstractmethod
235 def get_load_parameters(cls) -> List[ParameterName]:
236 """
237 Returns:
238 List of all parameters to query when reading eNodeB state
239 """
240 pass
241
242 @classmethod
243 @abstractmethod
244 def get_num_plmns(cls) -> int:
245 """
246 Returns:
247 The number of PLMNs in the configuration.
248 """
249 pass
250
251 @classmethod
252 @abstractmethod
253 def get_parameter_names(cls) -> List[ParameterName]:
254 """
255 Returns:
256 A list of all parameter names that are neither numbered objects,
257 or belonging to numbered objects
258 """
259 pass
260
261 @classmethod
262 @abstractmethod
263 def get_numbered_param_names(
264 cls,
265 ) -> Dict[ParameterName, List[ParameterName]]:
266 """
267 Returns:
268 A key for all parameters that are numbered objects, and the value
269 is the list of parameters that belong to that numbered object
270 """
271 pass