Init commit for standalone enodebd

Change-Id: I88eeef5135dd7ba8551ddd9fb6a0695f5325337b
diff --git a/configuration/service_configs.py b/configuration/service_configs.py
new file mode 100644
index 0000000..c60c340
--- /dev/null
+++ b/configuration/service_configs.py
@@ -0,0 +1,152 @@
+"""
+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.
+"""
+
+import logging
+import os
+from typing import Optional  # noqa: lint doesn't handle inline typehints
+from typing import Any, Dict
+
+import yaml
+from configuration.exceptions import LoadConfigError
+
+# Location of configs (both service config and mconfig)
+CONFIG_DIR = './magma_configs'
+CONFIG_OVERRIDE_DIR = './override_configs'
+
+
+def load_override_config(service_name: str) -> Optional[Any]:
+    """
+    Load override service configuration from the file in the override
+    directory.
+
+    Args:
+        service_name: service to pull configs for; name of config file
+
+    Returns: json-decoded value of the service config, None if it's not found
+
+    Raises:
+        LoadConfigError:
+            Unable to load config due to missing file or missing key
+    """
+    override_file_name = _override_file_name(service_name)
+    if os.path.isfile(override_file_name):
+        return _load_yaml_file(override_file_name)
+    return None
+
+
+def save_override_config(service_name: str, cfg: Any):
+    """
+    Write the configuration object to its corresponding file in the override
+    directory.
+
+    Args:
+        service_name: service to write config object to; name of config file
+        cfg: json-decoded value of the service config
+    """
+    override_file_name = _override_file_name(service_name)
+    os.makedirs(CONFIG_OVERRIDE_DIR, exist_ok=True)
+    with open(override_file_name, 'w', encoding='utf-8') as override_file:
+        yaml.dump(cfg, override_file, default_flow_style=False)
+
+
+def load_service_config(service_name: str) -> Any:
+    """
+    Load service configuration from file. Also check override directory,
+    and, if service file present there, override the values.
+
+    Args:
+        service_name: service to pull configs for; name of config file
+
+    Returns: json-decoded value of the service config
+
+    Raises:
+        LoadConfigError:
+            Unable to load config due to missing file or missing key
+    """
+    print(CONFIG_DIR, service_name)
+    cfg_file_name = os.path.join(CONFIG_DIR, '%s.yml' % service_name)
+    cfg = _load_yaml_file(cfg_file_name)
+
+    overrides = load_override_config(service_name)
+    if overrides is not None:
+        # Update the keys in the config if they are present in the override
+        cfg.update(overrides)
+    return cfg
+
+
+cached_service_configs = {}     # type: Dict[str, Any]
+
+
+def get_service_config_value(service: str, param: str, default: Any) -> Any:
+    """
+    Get a config value for :service:, falling back to a :default: value.
+
+    Log error if the default config is returned.
+
+    Args:
+        service: name of service to get config for
+        param: config key to fetch the value for
+        default: default value to return on failure
+
+    Returns:
+        value of :param: in the config files for :service:
+    """
+    service_configs = cached_service_configs.get(service)
+    try:
+        service_configs = service_configs or load_service_config(service)
+    except LoadConfigError as e:
+        logging.error('Error retrieving config: %s', e)
+        return default
+
+    # Handle empty file
+    if not service_configs:
+        logging.error('Error retrieving config, file empty for: %s', service)
+        return default
+
+    cached_service_configs[service] = service_configs
+
+    config_value = service_configs.get(param)
+    if config_value is not None:
+        return config_value
+    else:
+        logging.error(
+            'Error retrieving config for %s, key not found: %s',
+            service, param,
+        )
+        return default
+
+
+def _override_file_name(service_name: str) -> str:
+    return os.path.join(CONFIG_OVERRIDE_DIR, '%s.yml' % service_name)
+
+
+def _load_yaml_file(file_name: str) -> Any:
+    """
+    Load the yaml file and returns the python object.
+
+    Args:
+        file_name: name of the .yml file
+
+    Returns:
+        Contents of the yml file deserialized into a Python object
+
+    Raises:
+        LoadConfigError: on error
+    """
+
+    try:
+        with open(file_name, 'r', encoding='utf-8') as stream:
+            data = yaml.safe_load(stream)
+            return data
+    except (OSError, yaml.YAMLError) as e:
+        raise LoadConfigError('Error loading yml config') from e