blob: ecaff6f87b3f87b9090cac6815cefb1745a39123 [file] [log] [blame]
Wei-Yu Chenad55cb82022-02-15 20:07:01 +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
Wei-Yu Chen49950b92021-11-08 19:19:18 +08005
Wei-Yu Chen49950b92021-11-08 19:19:18 +08006from abc import ABC, abstractmethod
7from asyncio import BaseEventLoop
8from time import time
9from typing import Any, Type
10
11from common.service import MagmaService
12from data_models.data_model import DataModel
13from data_models.data_model_parameters import ParameterName
14from device_config.enodeb_config_postprocessor import (
15 EnodebConfigurationPostProcessor,
16)
17from device_config.enodeb_configuration import EnodebConfiguration
18from devices.device_utils import EnodebDeviceName
19from state_machines.acs_state_utils import are_tr069_params_equal
20
21
22class EnodebAcsStateMachine(ABC):
23 """
24 Handles all TR-069 messages.
25 Acts as the Auto Configuration Server (ACS), as specified by TR-069.
26 A device/version specific ACS message handler.
27 Different devices have various idiosyncrasies.
28 Subclass BasicEnodebAcsStateMachine for a specific device/version
29 implementation.
30
31 This ACS class can only handle a single connected eNodeB device.
32 Multiple connected eNodeB devices will lead to undefined behavior.
33
34 This ABC is more of an interface definition.
35 """
36
37 def __init__(self, use_param_key: bool = False) -> None:
38 self._service = None
39 self._desired_cfg = None
40 self._device_cfg = None
41 self._data_model = None
42 self._are_invasive_changes_applied = True
43 # Flag to preseve backwards compatibility
44 self._use_param_key = use_param_key
45 self._param_version_key = None
46
47 def has_parameter(self, param: ParameterName) -> bool:
48 """
49 Return True if the data model has the parameter
50
51 Raise KeyError if the parameter is optional and we do not know yet
52 if this eNodeB has the parameter
53 """
54 return self.data_model.is_parameter_present(param)
55
56 def get_parameter(self, param: ParameterName) -> Any:
57 """
58 Return the value of the parameter
59 """
60 return self.device_cfg.get_parameter(param)
61
62 def set_parameter_asap(self, param: ParameterName, value: Any) -> None:
63 """
64 Set the parameter to the suggested value ASAP
65 """
66 self.desired_cfg.set_parameter(param, value)
67
68 def is_enodeb_configured(self) -> bool:
69 """
70 True if the desired configuration matches the device configuration
71 """
72 if self.desired_cfg is None:
73 return False
74 if not self.data_model.are_param_presences_known():
75 return False
76 desired = self.desired_cfg.get_parameter_names()
77
78 for name in desired:
79 val1 = self.desired_cfg.get_parameter(name)
80 val2 = self.device_cfg.get_parameter(name)
81 type_ = self.data_model.get_parameter(name).type
82 if not are_tr069_params_equal(val1, val2, type_):
83 return False
84
85 for obj_name in self.desired_cfg.get_object_names():
86 params = self.desired_cfg.get_parameter_names_for_object(obj_name)
87 for name in params:
88 val1 = self.device_cfg.get_parameter_for_object(name, obj_name)
89 val2 = self.desired_cfg.get_parameter_for_object(
90 name,
91 obj_name,
92 )
93 type_ = self.data_model.get_parameter(name).type
94 if not are_tr069_params_equal(val1, val2, type_):
95 return False
96 return True
97
98 @abstractmethod
99 def get_state(self) -> str:
100 """
101 Get info about the state of the ACS
102 """
103 pass
104
105 @abstractmethod
106 def handle_tr069_message(self, message: Any) -> Any:
107 """
108 Given a TR-069 message sent from the hardware, return an
109 appropriate response
110 """
111 pass
112
113 @abstractmethod
114 def transition(self, next_state: str) -> None:
115 pass
116
117 @property
118 def service(self) -> MagmaService:
119 return self._service
120
121 @service.setter
122 def service(self, service: MagmaService) -> None:
123 self._service = service
124
125 @property
126 def event_loop(self) -> BaseEventLoop:
127 return self._service.loop
128
129 @property
130 def mconfig(self) -> Any:
131 return self._service.mconfig
132
133 @property
134 def service_config(self) -> Any:
135 return self._service.config
136
137 @property
138 def desired_cfg(self) -> EnodebConfiguration:
139 return self._desired_cfg
140
141 @desired_cfg.setter
142 def desired_cfg(self, val: EnodebConfiguration) -> None:
143 if self.has_version_key:
144 self.parameter_version_inc()
145 self._desired_cfg = val
146
147 @property
148 def device_cfg(self) -> EnodebConfiguration:
149 return self._device_cfg
150
151 @device_cfg.setter
152 def device_cfg(self, val: EnodebConfiguration) -> None:
153 self._device_cfg = val
154
155 @property
156 def data_model(self) -> DataModel:
157 return self._data_model
158
159 @property
160 def has_version_key(self) -> bool:
161 """ Return if the ACS supports param version key """
162 return self._use_param_key
163
164 @property
165 def parameter_version_key(self) -> int:
166 """ Return the param version key """
167 return self._param_version_key
168
169 def parameter_version_inc(self):
170 """ Set the internal version key to the timestamp """
171 self._param_version_key = time()
172
173 @data_model.setter
174 def data_model(self, data_model) -> None:
175 self._data_model = data_model
176
177 @property
178 def are_invasive_changes_applied(self) -> bool:
179 return self._are_invasive_changes_applied
180
181 @are_invasive_changes_applied.setter
182 def are_invasive_changes_applied(self, is_applied: bool) -> None:
183 self._are_invasive_changes_applied = is_applied
184
185 @property
186 @abstractmethod
187 def data_model_class(self) -> Type[DataModel]:
188 pass
189
190 @property
191 @abstractmethod
192 def device_name(self) -> EnodebDeviceName:
193 pass
194
195 @property
196 @abstractmethod
197 def config_postprocessor(self) -> EnodebConfigurationPostProcessor:
198 pass
199
200 @abstractmethod
201 def reboot_asap(self) -> None:
202 """
203 Send a request to reboot the eNodeB ASAP
204 """
205 pass
206
207 @abstractmethod
208 def is_enodeb_connected(self) -> bool:
209 pass
210
211 @abstractmethod
212 def stop_state_machine(self) -> None:
213 pass