Init commit for standalone enodebd
Change-Id: I88eeef5135dd7ba8551ddd9fb6a0695f5325337b
diff --git a/state_machines/enb_acs.py b/state_machines/enb_acs.py
new file mode 100644
index 0000000..24e24e7
--- /dev/null
+++ b/state_machines/enb_acs.py
@@ -0,0 +1,220 @@
+"""
+Copyright 2020 The Magma Authors.
+
+This source code is licensed under the BSD-style license found in the
+LICENSE file in the root directory of this source tree.
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+from abc import ABC, abstractmethod
+from asyncio import BaseEventLoop
+from time import time
+from typing import Any, Type
+
+from common.service import MagmaService
+from data_models.data_model import DataModel
+from data_models.data_model_parameters import ParameterName
+from device_config.enodeb_config_postprocessor import (
+ EnodebConfigurationPostProcessor,
+)
+from device_config.enodeb_configuration import EnodebConfiguration
+from devices.device_utils import EnodebDeviceName
+from state_machines.acs_state_utils import are_tr069_params_equal
+
+
+class EnodebAcsStateMachine(ABC):
+ """
+ Handles all TR-069 messages.
+ Acts as the Auto Configuration Server (ACS), as specified by TR-069.
+ A device/version specific ACS message handler.
+ Different devices have various idiosyncrasies.
+ Subclass BasicEnodebAcsStateMachine for a specific device/version
+ implementation.
+
+ This ACS class can only handle a single connected eNodeB device.
+ Multiple connected eNodeB devices will lead to undefined behavior.
+
+ This ABC is more of an interface definition.
+ """
+
+ def __init__(self, use_param_key: bool = False) -> None:
+ self._service = None
+ self._desired_cfg = None
+ self._device_cfg = None
+ self._data_model = None
+ self._are_invasive_changes_applied = True
+ # Flag to preseve backwards compatibility
+ self._use_param_key = use_param_key
+ self._param_version_key = None
+
+ def has_parameter(self, param: ParameterName) -> bool:
+ """
+ Return True if the data model has the parameter
+
+ Raise KeyError if the parameter is optional and we do not know yet
+ if this eNodeB has the parameter
+ """
+ return self.data_model.is_parameter_present(param)
+
+ def get_parameter(self, param: ParameterName) -> Any:
+ """
+ Return the value of the parameter
+ """
+ return self.device_cfg.get_parameter(param)
+
+ def set_parameter_asap(self, param: ParameterName, value: Any) -> None:
+ """
+ Set the parameter to the suggested value ASAP
+ """
+ self.desired_cfg.set_parameter(param, value)
+
+ def is_enodeb_configured(self) -> bool:
+ """
+ True if the desired configuration matches the device configuration
+ """
+ if self.desired_cfg is None:
+ return False
+ if not self.data_model.are_param_presences_known():
+ return False
+ desired = self.desired_cfg.get_parameter_names()
+
+ for name in desired:
+ val1 = self.desired_cfg.get_parameter(name)
+ val2 = self.device_cfg.get_parameter(name)
+ type_ = self.data_model.get_parameter(name).type
+ if not are_tr069_params_equal(val1, val2, type_):
+ return False
+
+ for obj_name in self.desired_cfg.get_object_names():
+ params = self.desired_cfg.get_parameter_names_for_object(obj_name)
+ for name in params:
+ val1 = self.device_cfg.get_parameter_for_object(name, obj_name)
+ val2 = self.desired_cfg.get_parameter_for_object(
+ name,
+ obj_name,
+ )
+ type_ = self.data_model.get_parameter(name).type
+ if not are_tr069_params_equal(val1, val2, type_):
+ return False
+ return True
+
+ @abstractmethod
+ def get_state(self) -> str:
+ """
+ Get info about the state of the ACS
+ """
+ pass
+
+ @abstractmethod
+ def handle_tr069_message(self, message: Any) -> Any:
+ """
+ Given a TR-069 message sent from the hardware, return an
+ appropriate response
+ """
+ pass
+
+ @abstractmethod
+ def transition(self, next_state: str) -> None:
+ pass
+
+ @property
+ def service(self) -> MagmaService:
+ return self._service
+
+ @service.setter
+ def service(self, service: MagmaService) -> None:
+ self._service = service
+
+ @property
+ def event_loop(self) -> BaseEventLoop:
+ return self._service.loop
+
+ @property
+ def mconfig(self) -> Any:
+ return self._service.mconfig
+
+ @property
+ def service_config(self) -> Any:
+ return self._service.config
+
+ @property
+ def desired_cfg(self) -> EnodebConfiguration:
+ return self._desired_cfg
+
+ @desired_cfg.setter
+ def desired_cfg(self, val: EnodebConfiguration) -> None:
+ if self.has_version_key:
+ self.parameter_version_inc()
+ self._desired_cfg = val
+
+ @property
+ def device_cfg(self) -> EnodebConfiguration:
+ return self._device_cfg
+
+ @device_cfg.setter
+ def device_cfg(self, val: EnodebConfiguration) -> None:
+ self._device_cfg = val
+
+ @property
+ def data_model(self) -> DataModel:
+ return self._data_model
+
+ @property
+ def has_version_key(self) -> bool:
+ """ Return if the ACS supports param version key """
+ return self._use_param_key
+
+ @property
+ def parameter_version_key(self) -> int:
+ """ Return the param version key """
+ return self._param_version_key
+
+ def parameter_version_inc(self):
+ """ Set the internal version key to the timestamp """
+ self._param_version_key = time()
+
+ @data_model.setter
+ def data_model(self, data_model) -> None:
+ self._data_model = data_model
+
+ @property
+ def are_invasive_changes_applied(self) -> bool:
+ return self._are_invasive_changes_applied
+
+ @are_invasive_changes_applied.setter
+ def are_invasive_changes_applied(self, is_applied: bool) -> None:
+ self._are_invasive_changes_applied = is_applied
+
+ @property
+ @abstractmethod
+ def data_model_class(self) -> Type[DataModel]:
+ pass
+
+ @property
+ @abstractmethod
+ def device_name(self) -> EnodebDeviceName:
+ pass
+
+ @property
+ @abstractmethod
+ def config_postprocessor(self) -> EnodebConfigurationPostProcessor:
+ pass
+
+ @abstractmethod
+ def reboot_asap(self) -> None:
+ """
+ Send a request to reboot the eNodeB ASAP
+ """
+ pass
+
+ @abstractmethod
+ def is_enodeb_connected(self) -> bool:
+ pass
+
+ @abstractmethod
+ def stop_state_machine(self) -> None:
+ pass