AETHER-3573 Simplify configuration files in enodebd

Change-Id: I048d743c0677c85244b87a6c6444f39c06f6bf4b
diff --git a/VERSION b/VERSION
index 0c62199..f477849 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.2.1
+0.2.2
\ No newline at end of file
diff --git a/configuration/service_configs.py b/configuration/service_configs.py
index 4d83fe8..44f89ea 100644
--- a/configuration/service_configs.py
+++ b/configuration/service_configs.py
@@ -77,7 +77,7 @@
 
 def load_enb_config() -> Any:
     """
-    Load enb configurations from directory.
+    Load the specific serial number enoode configuration as a dictionary
 
     Args:
         None
diff --git a/data_models/data_model_parameters.py b/data_models/data_model_parameters.py
index f211ef3..f1621a9 100644
--- a/data_models/data_model_parameters.py
+++ b/data_models/data_model_parameters.py
@@ -21,7 +21,7 @@
     SW_VERSION = 'SW version'
 
     SERIAL_NUMBER = 'Serial number'
-    CELL_ID = 'Cell ID'
+    CELL_ID = 'cell_id'
     IP_ADDRESS = "ip_address"
 
     # Capabilities
@@ -38,10 +38,12 @@
 
     FREQ_BAND_1 = "frequency_band_1"
     FREQ_BAND_2 = "frequency_band_2"
-    FREQ_BAND_LIST = "freq_band_list"
+    FREQ_BAND_LIST = "frequency_band_list"
 
     BAND = 'Band'
-    PCI = 'PCI'
+    PCI_LIST = 'pci_list'
+    PCI1 = 'pci1'
+    PCI2 = 'pci2'
     DL_BANDWIDTH = 'downlink_bandwidth'
     UL_BANDWIDTH = 'uplink_bandwidth'
     TX_POWER = "tx_power"
@@ -64,15 +66,15 @@
 
     # Cell parameters
     CELL_ENABLE64QAM = "enable64qam"
-    SPECIAL_SUBFRAME_PATTERN = 'subframe_configuration'
+    SPECIAL_SUBFRAME_PATTERN = 'special_subframe_pattern'
     SUBFRAME_ASSIGNMENT = 'subframe_assignment'
 
     # Core network parameters
-    MME_IP = 'MME IP'
-    MME_PORT = 'MME port'
+    MME_ADDRESS = 'mme_address'
+    MME_PORT = 'mme_port'
     NUM_PLMNS = 'Num PLMNs'
     PLMN = 'PLMN'
-    PLMN_LIST = 'PLMN List'
+    PLMN_LIST = 'plmn_list'
 
     # PLMN parameters
     PLMN_N = 'PLMN %d'
@@ -82,7 +84,7 @@
     PLMN_N_PLMNID = 'PLMN %d PLMNID'
 
     # PLMN arrays are added below
-    TAC = 'tac'
+    TAC1 = 'tac1'
     TAC2 = 'tac2'
     IP_SEC_ENABLE = 'ipsec_enable'
     MME_POOL_ENABLE = 'mme_pool_enable'
@@ -126,6 +128,19 @@
     FIRMWARE_URL = "firmware_url"
     FIRMWARE_SIZE = "firmware_size"
 
+    @classmethod
+    def all_paramters(cls):
+        """ Return all possible parameter in the structure """
+        ret_dict = dict()
+        for item in filter(lambda x: x[:2] != "__", cls.__dict__.keys()):
+            if not callable(getattr(cls, item)):
+                # The return format is a reversed dictionary
+                # {'cell_id': 'CELL_ID', ...}
+                ret_dict[getattr(cls, item)] = item
+
+        return ret_dict
+        
+
 
 class TrParameterType():
     BOOLEAN = 'boolean'
diff --git a/device_config/configuration_init.py b/device_config/configuration_init.py
index 8acb152..97ce399 100644
--- a/device_config/configuration_init.py
+++ b/device_config/configuration_init.py
@@ -3,6 +3,7 @@
 #
 # SPDX-License-Identifier: BSD-3-Clause
 
+from distutils.sysconfig import customize_compiler
 import json
 from collections import namedtuple
 from typing import Any, Optional, Union
@@ -53,8 +54,6 @@
 
 
 def build_desired_config(
-        mconfig: Any,
-        service_config: Any,
         device_config: EnodebConfiguration,
         data_model: DataModel,
         post_processor: EnodebConfigurationPostProcessor,
@@ -66,124 +65,32 @@
     current state of configuration for that device, as well as what
     configuration we want to set on the device.
     Args:
-        mconfig: Managed configuration, eNodeB protobuf message
-        service_config:
+        device_config: The current configuration of the device.
+        data_model: The data model for the device.
+        post_processor: The post-processor to use for the device.
     Returns:
         Desired data model configuration for the device
     """
 
-    print("DEVICE CFG: ", device_config)
+    # The configuration we want to push to the target device
+    desired_configuration = EnodebConfiguration(data_model)
+    # The configuration read from local configuration files by serial number specific
+    customized_configuration = _get_enb_config(device_config)
+    # device_config is the current configuration read from the target device
 
-    cfg_desired = EnodebConfiguration(data_model)
+    # Check if the customized configuration is valid
+    desired_configuration.check_desired_configuration(device_config, customized_configuration)
+    # Apply the customized configuration to the target device
+    desired_configuration.apply_desired_configuration(device_config, customized_configuration)
 
-    # Determine configuration parameters
-    _set_management_server(cfg_desired)
+    post_processor.postprocess(desired_configuration)
 
-    # Attempt to load device configuration from YANG before service mconfig
-    enb_config = _get_enb_yang_config(device_config) or \
-                 _get_enb_config(mconfig, device_config)
-
-    print(enb_config)
-
-    _set_earfcn_freq_band_mode(
-        device_config, cfg_desired, data_model,
-        enb_config.earfcndl,
-    )
-    if enb_config.subframe_assignment is not None:
-        _set_tdd_subframe_config(
-            device_config, cfg_desired,
-            enb_config.subframe_assignment,
-            enb_config.special_subframe_pattern,
-        )
-    _set_pci(cfg_desired, enb_config.pci)
-    _set_plmnids_tac(cfg_desired, enb_config.plmnid_list, enb_config.tac)
-    _set_bandwidth(cfg_desired, data_model, enb_config.bandwidth_mhz)
-    _set_cell_id(cfg_desired, enb_config.cell_id)
-    _set_perf_mgmt(
-        cfg_desired,
-        get_ip_from_if(service_config['tr069']['interface']),
-        service_config['tr069']['perf_mgmt_port'],
-    )
-    _set_misc_static_params(device_config, cfg_desired, data_model)
-    if enb_config.mme_address is not None and enb_config.mme_port is not None:
-        _set_s1_connection(
-            cfg_desired,
-            enb_config.mme_address,
-            enb_config.mme_port,
-        )
-    else:
-        _set_s1_connection(
-            cfg_desired, get_ip_from_if(service_config['s1_interface']),
-        )
-
-    # Enable LTE if we should
-    cfg_desired.set_parameter(
-        ParameterName.ADMIN_STATE,
-        enb_config.allow_enodeb_transmit,
-    )
-
-    post_processor.postprocess(mconfig, service_config, cfg_desired)
-    return cfg_desired
-
-
-def _get_enb_yang_config(
-        device_config: EnodebConfiguration,
-) -> Optional[SingleEnodebConfig]:
-    """"
-    Proof of concept configuration function to load eNB configs from YANG
-    data model. Attempts to load configuration from YANG for the eNodeB if
-    an entry exists with a matching serial number.
-    Args:
-        device_config: eNodeB device configuration
-    Returns:
-        None or a SingleEnodebConfig from YANG with matching serial number
-    """
-    enb = []
-    mme_list = []
-    mme_address = None
-    mme_port = None
-    try:
-        enb_serial = \
-            device_config.get_parameter(ParameterName.SERIAL_NUMBER)
-        config = json.loads(
-            load_service_mconfig_as_json('yang').get('value', '{}'),
-        )
-
-        enb.extend(
-            filter(
-                lambda entry: entry['serial'] == enb_serial,
-                config.get('cellular', {}).get('enodeb', []),
-            ),
-        )
-    except (ValueError, KeyError, LoadConfigError):
-        return None
-    if len(enb) == 0:
-        return None
-    enb_config = enb[0].get('config', {})
-    mme_list.extend(enb_config.get('mme', []))
-    if len(mme_list) > 0:
-        mme_address = mme_list[0].get('host')
-        mme_port = mme_list[0].get('port')
-    single_enodeb_config = SingleEnodebConfig(
-        earfcndl=enb_config.get('earfcndl'),
-        subframe_assignment=enb_config.get('subframe_assignment'),
-        special_subframe_pattern=enb_config.get('special_subframe_pattern'),
-        pci=enb_config.get('pci'),
-        plmnid_list=",".join(enb_config.get('plmnid', [])),
-        tac=enb_config.get('tac'),
-        bandwidth_mhz=enb_config.get('bandwidth_mhz'),
-        cell_id=enb_config.get('cell_id'),
-        allow_enodeb_transmit=enb_config.get('transmit_enabled'),
-        mme_address=mme_address,
-        mme_port=mme_port,
-    )
-    return single_enodeb_config
+    return desired_configuration
 
 
 def _get_enb_config(
-        mconfig: mconfigs_pb2.EnodebD,
-        device_config: EnodebConfiguration,
-) -> SingleEnodebConfig:
+    device_config: EnodebConfiguration,
+) -> dict:
     # The eNodeB parameters to be generated with default value,
     # It will load from eNB configs based on serial number or default value
     # The params is a nested list which contains 2 format of parameter names.
@@ -191,368 +98,24 @@
     #   magma_configs/serial_number/ and magma_configs/acs_common.yml
     # The second parameter is the name of gateway configuration in 
     #   override_configs/gateway.mconfig
-
-    # params.column1 = SingleEnodebConfig key
-    # params.column2 = sn config
-    # params.column3 = common_config
-    # params.column4 = mconfig
-
-    params = [
-        ["earfcndl", "earfcn_downlink1", "earfcndl" * 2],
-        ["subframe_assignment", "subframe_assignment", "subframeAssignment", "subframe_assignment"],
-        ["special_subframe_pattern", "subframe_configuration", "specialSubframePattern", "special_subframe_pattern"],
-        ["pci", "pci1", "pci", "pci"],
-        ["plmnid_list", "plmnid_list", "plmnidList", "plmnid_list"],
-        ["tac", "tac1", "tac", "tac"],
-        ["bandwidth_mhz", "downlink_bandwidth", "bandwidthMhz", "bandwidth_mhz"],
-        # Note: mconfig doesn't have allowEnodebTransmit
-        ["allow_enodeb_transmit", "allow_enodeb_transmit", "allowEnodebTransmit", "allowEnodebTransmit"]
-    ]
-    
-    extend_params = ["cell_id", "mme_address", "mme_port"]
+    def flatten_dictionary(dictionary: dict) -> dict:
+        return {k: v for item in dictionary.values() for k, v in item.items()}
 
     params_dict = dict()
 
-    common_config = load_common_config()
+    # The common configuration file is loaded from magma_configs/acs_common.yml
+    common_config = flatten_dictionary(load_common_config())
+    
+    # The serial number configuration file is loaded from magma_configs/serial_number/
+    # Get the specific configuration with the serial number as the key
     enb_configs = load_enb_config()
     enb_serial = device_config.get_parameter(ParameterName.SERIAL_NUMBER)
     enb_config = enb_configs.get(enb_serial, dict())
 
-    for param in params:
-        params_dict[param[0]] = enb_config.get(param[1], 
-            common_config.get(param[0], mconfig.__getattribute__(param[0]))
-        )
+    generated_config = dict()
+    for parameter_name in ParameterName.all_paramters():
+        parameter_value = enb_config.get(parameter_name, common_config.get(parameter_name))
+        if parameter_value is not None:
+            generated_config[parameter_name] = parameter_value
 
-    for param in extend_params:
-        params_dict[param] = enb_config.get(param, common_config.get(param, None))
-
-    return SingleEnodebConfig(**params_dict)
-
-
-def _set_pci(
-        cfg: EnodebConfiguration,
-        pci: Any,
-) -> None:
-    """
-    Set the following parameters:
-     - PCI
-    """
-
-    if pci is int and pci not in range(0, 504 + 1):
-        raise ConfigurationError('Invalid PCI (%d)' % pci)
-
-    if pci is str and any(map(lambda x: int(x) not in range(0, 504 + 1), pci.split(","))):
-        raise ConfigurationError('Invalid PCI (%s)' % pci)
-
-    cfg.set_parameter(ParameterName.PCI, pci)
-
-
-def _set_bandwidth(
-        cfg: EnodebConfiguration,
-        data_model: DataModel,
-        bandwidth_mhz: Any,
-) -> None:
-    """
-    Set the following parameters:
-     - DL bandwidth
-     - UL bandwidth
-    """
-    _set_param_if_present(
-        cfg, data_model, ParameterName.DL_BANDWIDTH,
-        bandwidth_mhz,
-    )
-    _set_param_if_present(
-        cfg, data_model, ParameterName.UL_BANDWIDTH,
-        bandwidth_mhz,
-    )
-
-
-def _set_cell_id(
-        cfg: EnodebConfiguration,
-        cell_id: int,
-) -> None:
-    config_assert(
-        cell_id in range(0, 268435456),
-        'Cell Identity should be from 0 - (2^28 - 1)',
-    )
-    cfg.set_parameter(ParameterName.CELL_ID, cell_id)
-
-
-def _set_tdd_subframe_config(
-        device_cfg: EnodebConfiguration,
-        cfg: EnodebConfiguration,
-        subframe_assignment: Any,
-        special_subframe_pattern: Any,
-) -> None:
-    """
-    Set the following parameters:
-     - Subframe assignment
-     - Special subframe pattern
-    """
-    # Don't try to set if this is not TDD mode
-    if (
-        device_cfg.has_parameter(ParameterName.DUPLEX_MODE_CAPABILITY)
-            and device_cfg.get_parameter(ParameterName.DUPLEX_MODE_CAPABILITY)
-            != 'TDDMode'
-    ):
-        return
-
-    config_assert(
-        subframe_assignment in range(0, 6 + 1),
-        'Invalid TDD subframe assignment (%d)' % subframe_assignment,
-    )
-    config_assert(
-        special_subframe_pattern in range(0, 9 + 1),
-        'Invalid TDD special subframe pattern (%d)'
-        % special_subframe_pattern,
-    )
-
-    cfg.set_parameter(
-        ParameterName.SUBFRAME_ASSIGNMENT,
-        subframe_assignment,
-    )
-    cfg.set_parameter(
-        ParameterName.SPECIAL_SUBFRAME_PATTERN,
-        special_subframe_pattern,
-    )
-
-
-def _set_management_server(cfg: EnodebConfiguration) -> None:
-    """
-    Set the following parameters:
-     - Periodic inform enable
-     - Periodic inform interval (hard-coded)
-    """
-    cfg.set_parameter(ParameterName.PERIODIC_INFORM_ENABLE, True)
-    # In seconds
-    cfg.set_parameter(ParameterName.PERIODIC_INFORM_INTERVAL, 5)
-
-
-def _set_s1_connection(
-        cfg: EnodebConfiguration,
-        mme_ip: Any,
-        mme_port: Any = DEFAULT_S1_PORT,
-) -> None:
-    """
-    Set the following parameters:
-     - MME IP
-     - MME port (defalts to 36412 as per TR-196 recommendation)
-    """
-    config_assert(type(mme_ip) == str, 'Invalid MME IP type')
-    config_assert(type(mme_port) == int, 'Invalid MME Port type')
-    cfg.set_parameter(ParameterName.MME_IP, mme_ip)
-    cfg.set_parameter(ParameterName.MME_PORT, mme_port)
-
-
-def _set_perf_mgmt(
-        cfg: EnodebConfiguration,
-        perf_mgmt_ip: str,
-        perf_mgmt_port: int,
-) -> None:
-    """
-    Set the following parameters:
-     - Perf mgmt enable
-     - Perf mgmt upload interval
-     - Perf mgmt upload URL
-    """
-    cfg.set_parameter(ParameterName.PERF_MGMT_ENABLE, True)
-    # Upload interval supported values (in secs):
-    # [60, 300, 900, 1800, 3600]
-    # Note: eNodeB crashes have been experienced with 60-sec interval.
-    # Hence using 300sec
-    cfg.set_parameter(
-        ParameterName.PERF_MGMT_UPLOAD_INTERVAL,
-        300,
-    )
-    cfg.set_parameter(
-        ParameterName.PERF_MGMT_UPLOAD_URL,
-        'http://%s:%d/' % (perf_mgmt_ip, perf_mgmt_port),
-    )
-
-
-def _set_misc_static_params(
-        device_cfg: EnodebConfiguration,
-        cfg: EnodebConfiguration,
-        data_model: DataModel,
-) -> None:
-    """
-    Set the following parameters:
-     - Local gateway enable
-     - GPS enable
-    """
-    _set_param_if_present(
-        cfg, data_model, ParameterName.LOCAL_GATEWAY_ENABLE,
-        0,
-    )
-    _set_param_if_present(cfg, data_model, ParameterName.GPS_ENABLE, True)
-    # For BaiCells eNodeBs, IPSec enable may be either integer or bool.
-    # Set to false/0 depending on the current type
-    if data_model.is_parameter_present(ParameterName.IP_SEC_ENABLE):
-        try:
-            int(device_cfg.get_parameter(ParameterName.IP_SEC_ENABLE))
-            cfg.set_parameter(ParameterName.IP_SEC_ENABLE, value=0)
-        except ValueError:
-            cfg.set_parameter(ParameterName.IP_SEC_ENABLE, value=False)
-
-    _set_param_if_present(cfg, data_model, ParameterName.CELL_RESERVED, False)
-    _set_param_if_present(
-        cfg, data_model, ParameterName.MME_POOL_ENABLE,
-        False,
-    )
-
-
-def _set_plmnids_tac(
-        cfg: EnodebConfiguration,
-        plmnids: Union[int, str],
-        tac: Any,
-) -> None:
-    """
-    Set the following parameters:
-     - PLMNID list (including all child parameters)
-
-    Input 'plmnids' is comma-separated list of PLMNIDs
-    """
-    # Convert int PLMNID to string
-    if type(plmnids) == int:
-        plmnid_str = str(plmnids)
-    else:
-        config_assert(type(plmnids) == str, 'PLMNID must be string')
-        plmnid_str = plmnids
-
-    # Multiple PLMNIDs will be supported using comma-separated list.
-    # Currently, just one supported
-    for char in plmnid_str:
-        config_assert(
-            char in '0123456789, ',
-            'Unhandled character (%s) in PLMNID' % char,
-        )
-    plmnid_list = plmnid_str.split(',')
-
-    # TODO - add support for multiple PLMNIDs
-    config_assert(
-        len(plmnid_list) == 1,
-        'Exactly one PLMNID must be configured',
-    )
-
-    # Validate PLMNIDs
-    plmnid_list[0] = plmnid_list[0].strip()
-    config_assert(
-        len(plmnid_list[0]) <= 6,
-        'PLMNID must be length <=6 (%s)' % plmnid_list[0],
-    )
-
-    # We just need one PLMN element in the config. Delete all others.
-    for i in range(1, 2):  # data_model.get_num_plmns() + 1):
-        object_name = ParameterName.PLMN_N % i
-        enable_plmn = i == 1
-        cfg.add_object(object_name)
-        cfg.set_parameter_for_object(
-            ParameterName.PLMN_N_ENABLE % i,
-            enable_plmn,
-            object_name,
-        )
-        if enable_plmn:
-            cfg.set_parameter_for_object(
-                ParameterName.PLMN_N_CELL_RESERVED % i,
-                False, object_name,
-            )
-            cfg.set_parameter_for_object(
-                ParameterName.PLMN_N_PRIMARY % i,
-                enable_plmn,
-                object_name,
-            )
-            cfg.set_parameter_for_object(
-                ParameterName.PLMN_N_PLMNID % i,
-                plmnid_list[i - 1],
-                object_name,
-            )
-    cfg.set_parameter(ParameterName.TAC, tac)
-
-
-def _set_earfcn_freq_band_mode(
-        device_cfg: EnodebConfiguration,
-        cfg: EnodebConfiguration,
-        data_model: DataModel,
-        earfcndl: int,
-) -> None:
-    """
-    Set the following parameters:
-     - EARFCNDL
-     - EARFCNUL
-     - Band
-    """
-    # Note: validation of EARFCNDL done by mapping function. If invalid
-    # EARFCN, raise ConfigurationError
-    try:
-        band, duplex_mode, earfcnul = map_earfcndl_to_band_earfcnul_mode(
-            earfcndl,
-        )
-    except ValueError as err:
-        raise ConfigurationError(err)
-
-    # Verify capabilities
-    if device_cfg.has_parameter(ParameterName.DUPLEX_MODE_CAPABILITY):
-        duplex_capability = \
-            device_cfg.get_parameter(ParameterName.DUPLEX_MODE_CAPABILITY)
-        if duplex_mode == DuplexMode.TDD and duplex_capability != 'TDDMode':
-            raise ConfigurationError((
-                'eNodeB duplex mode capability is <{0}>, '
-                'but earfcndl is <{1}>, giving duplex '
-                'mode <{2}> instead'
-            ).format(
-                duplex_capability, str(earfcndl), str(duplex_mode),
-            ))
-        elif duplex_mode == DuplexMode.FDD and duplex_capability != 'FDDMode':
-            raise ConfigurationError((
-                'eNodeB duplex mode capability is <{0}>, '
-                'but earfcndl is <{1}>, giving duplex '
-                'mode <{2}> instead'
-            ).format(
-                duplex_capability, str(earfcndl), str(duplex_mode),
-            ))
-        elif duplex_mode not in {DuplexMode.TDD, DuplexMode.FDD}:
-            raise ConfigurationError(
-                'Invalid duplex mode (%s)' % str(duplex_mode),
-            )
-
-    if device_cfg.has_parameter(ParameterName.BAND_CAPABILITY):
-        # Baicells indicated that they no longer use the band capability list,
-        # so it may not be populated correctly
-        band_capability_list = device_cfg.get_parameter(
-            ParameterName.BAND_CAPABILITY,
-        )
-        band_capabilities = band_capability_list.split(',')
-        if str(band) not in band_capabilities:
-            logger.warning(
-                'Band %d not in capabilities list (%s). Continuing'
-                ' with config because capabilities list may not be'
-                ' correct', band, band_capabilities,
-            )
-    cfg.set_parameter(ParameterName.EARFCNDL, earfcndl)
-    if duplex_mode == DuplexMode.FDD:
-        _set_param_if_present(
-            cfg, data_model, ParameterName.EARFCNUL,
-            earfcnul,
-        )
-    else:
-        logger.debug('Not setting EARFCNUL - duplex mode is not FDD')
-
-    _set_param_if_present(cfg, data_model, ParameterName.BAND, band)
-
-    if duplex_mode == DuplexMode.TDD:
-        logger.debug('Set EARFCNDL=%d, Band=%d', earfcndl, band)
-    elif duplex_mode == DuplexMode.FDD:
-        logger.debug(
-            'Set EARFCNDL=%d, EARFCNUL=%d, Band=%d',
-            earfcndl, earfcnul, band,
-        )
-
-
-def _set_param_if_present(
-        cfg: EnodebConfiguration,
-        data_model: DataModel,
-        param: ParameterName,
-        value: Any,
-) -> None:
-    if data_model.is_parameter_present(param):
-        cfg.set_parameter(param, value)
+    return generated_config
diff --git a/device_config/enodeb_config_postprocessor.py b/device_config/enodeb_config_postprocessor.py
index e8aebc6..39c5d31 100644
--- a/device_config/enodeb_config_postprocessor.py
+++ b/device_config/enodeb_config_postprocessor.py
@@ -16,7 +16,7 @@
     """
 
     @abstractmethod
-    def postprocess(self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration) -> None:
+    def postprocess(self, desired_cfg: EnodebConfiguration) -> None:
         """
         Implementation of function which overrides the desired configuration
         for the eNodeB
diff --git a/device_config/enodeb_configuration.py b/device_config/enodeb_configuration.py
index 2b35271..8a7050b 100644
--- a/device_config/enodeb_configuration.py
+++ b/device_config/enodeb_configuration.py
@@ -8,10 +8,27 @@
 
 from data_models.data_model import DataModel
 from data_models.data_model_parameters import ParameterName
+
+from collections import namedtuple
+
+from lte_utils import DuplexMode, map_earfcndl_to_band_earfcnul_mode
+
 from exceptions import ConfigurationError
 from logger import EnodebdLogger as logger
 
 
+SingleEnodebConfig = namedtuple(
+    'SingleEnodebConfig',
+    [
+        'earfcndl', 'subframe_assignment',
+        'special_subframe_pattern',
+        'pci', 'plmnid_list', 'tac',
+        'bandwidth_mhz', 'cell_id',
+        'allow_enodeb_transmit',
+        'mme_address', 'mme_port',
+    ],
+)
+
 class EnodebConfiguration():
     """
     This represents the data model configuration for a single
@@ -81,6 +98,18 @@
         self._assert_param_in_model(param_name)
         self._param_to_value[param_name] = value
 
+    def set_parameter_if_present(self, parameter_name: ParameterName, value: Any) -> None:
+        """
+        Args:
+            param_name: the parameter name to configure
+            value: the value to set, formatted to be understood by enodebd
+        """
+
+        trparam_model = self.data_model
+        tr_param = trparam_model.get_parameter(parameter_name)
+        if tr_param is not None:
+            self._param_to_value[parameter_name] = value
+
     def delete_parameter(self, param_name: ParameterName) -> None:
         del self._param_to_value[param_name]
 
@@ -151,3 +180,123 @@
         if tr_param is None:
             logger.warning('Parameter <%s> not defined in model', param_name)
             raise ConfigurationError("Parameter %s not defined in model." % param_name)
+
+    def check_desired_configuration(self, current_config, desired_config: dict) -> bool:
+        def config_assert(condition: bool, message: str = None) -> None:
+            """ To be used in place of 'assert' so that ConfigurationError is raised
+                for all config-related exceptions. """
+            if not condition:
+                raise ConfigurationError(message)
+
+        # _set_earfcn_freq_band_mode
+        # Originally: 
+        # mconfig: loaded from proto definiation
+        # service_config: loaded from mconfig
+        # device_config: retrieved configuration from enodeb
+        # data_model: defined in device parameter
+    
+        # device_config = config loaded from enodeb, desired_config = config loaded from file
+
+        # _check_earfcn_freqw_band_mode
+        try:
+            band, duplex_mode, _ = map_earfcndl_to_band_earfcnul_mode(desired_config["earfcn_downlink1"])
+        except ValueError as err:
+            raise ConfigurationError(err)
+
+        if current_config.has_parameter(ParameterName.DUPLEX_MODE_CAPABILITY):
+            duplex_capability = current_config.get_parameter(ParameterName.DUPLEX_MODE_CAPABILITY)
+            if duplex_mode == DuplexMode.TDD and duplex_capability != "TDDMode":
+                raise ConfigurationError("Duplex mode TDD is not supported by eNodeB")
+            elif duplex_mode == DuplexMode.FDD and duplex_capability != "FDDMode":
+                raise ConfigurationError("Duplex mode FDD is not supported by eNodeB")
+            elif duplex_mode not in [DuplexMode.TDD, DuplexMode.FDD]:
+                raise ConfigurationError("Invalid duplex mode")
+        
+        if current_config.has_parameter(ParameterName.BAND_CAPABILITY):
+            band_capability = current_config.get_parameter(ParameterName.BAND_CAPABILITY).split(',')
+            if str(band) not in band_capability:
+                logger.warning("Band %d not in capability list %s", band, band_capability)
+
+        # _check_tdd_subframe_config
+        config_assert(
+            desired_config["subframe_assignment"] in range(0, 7),
+            "Invalid TDD special subframe assignment (%d)" % desired_config["subframe_assignment"],
+        )
+        config_assert(
+            desired_config["special_subframe_pattern"] in range(0, 10),
+            "Invalid TDD special subframe pattern (%d)" % desired_config["special_subframe_pattern"],
+        )
+        
+        # _check_plmnids_tac
+        for char in str(desired_config["plmn_list"]):
+            config_assert(char in "0123456789, ", "Invalid PLMNID (%s)" % desired_config["plmn_list"])
+        
+        # TODO - add support for multiple PLMNIDs
+        plmnid_list = str(desired_config["plmn_list"]).split(",")
+        config_assert(len(plmnid_list) == 1, "Only 1 PLMNID is supported")
+        config_assert(len(plmnid_list[0]) <= 6, "PLMNID must be length <= 6 (%s)" % plmnid_list[0])
+
+        # _check_s1_connection_configuration
+        config_assert(type(desired_config["mme_address"]) is str, "Invalid MME address")
+        config_assert(type(desired_config["mme_port"]) is int, "Invalid MME port")
+
+    def apply_desired_configuration(self, current_config, desired_config: SingleEnodebConfig) -> None:
+        
+        # _set_earfcn_freq_band_mode
+        self.set_parameter(ParameterName.EARFCNDL, desired_config["earfcn_downlink1"])
+        band, duplex_mode, _ = map_earfcndl_to_band_earfcnul_mode(desired_config["earfcn_downlink1"])
+        if duplex_mode == DuplexMode.FDD:
+            self.set_parameter(ParameterName.EARFCNUL, desired_config["earfcn_uplink1"])
+        self.set_parameter_if_present(ParameterName.BAND, band)
+
+        # _set_tdd_subframe_config
+        if (current_config.has_parameter(ParameterName.DUPLEX_MODE_CAPABILITY)
+            and current_config.get_parameter(ParameterName.DUPLEX_MODE_CAPABILITY) == "TDDMode"):
+            self.set_parameter(ParameterName.SUBFRAME_ASSIGNMENT, desired_config["subframe_assignment"])
+            self.set_parameter(ParameterName.SPECIAL_SUBFRAME_PATTERN, desired_config["special_subframe_pattern"])
+        
+        # _set_plmnids_tac
+        plmnid_list = str(desired_config["plmn_list"]).split(",")
+        for i in range(1, 2):
+            object_name = ParameterName.PLMN_N % i
+            enable_plmn = i == 1
+            self.add_object(object_name)
+            self.set_parameter_for_object(ParameterName.PLMN_N_ENABLE % i, enable_plmn, object_name)
+            if enable_plmn:
+                self.set_parameter_for_object(ParameterName.PLMN_N_CELL_RESERVED % i, False, object_name)
+                self.set_parameter_for_object(ParameterName.PLMN_N_PRIMARY % i, enable_plmn, object_name)
+                self.set_parameter_for_object(ParameterName.PLMN_N_PLMNID % i, plmnid_list[i - 1], object_name)
+        self.set_parameter(ParameterName.TAC1, desired_config["tac1"])
+
+        # _set_bandwidth
+        self.set_parameter(ParameterName.DL_BANDWIDTH, desired_config["downlink_bandwidth"])
+        self.set_parameter(ParameterName.UL_BANDWIDTH, desired_config["uplink_bandwidth"])
+
+        # _set_cell_id
+        self.set_parameter(ParameterName.CELL_ID, desired_config["cell_id"])
+
+        # _set_misc_static_params
+        self.set_parameter_if_present(ParameterName.LOCAL_GATEWAY_ENABLE, 0)
+        self.set_parameter_if_present(ParameterName.GPS_ENABLE, True)
+        self.set_parameter_if_present(ParameterName.IP_SEC_ENABLE, False)
+        self.set_parameter_if_present(ParameterName.CELL_RESERVED, False)
+        self.set_parameter_if_present(ParameterName.MME_POOL_ENABLE, False)
+
+        # _set_s1_connection_configuration
+        self.set_parameter(ParameterName.MME_ADDRESS, desired_config["mme_address"])
+        self.set_parameter(ParameterName.MME_PORT, desired_config["mme_port"])
+
+        # enable LTE if we should
+        self.set_parameter(ParameterName.ADMIN_STATE, desired_config["admin_state"])
+
+        # These parameters are already configured at above
+        exclude_list = [
+            "earfcn_downlink1", "earfcn_uplink1", "subframe_assignment", "special_subframe_pattern",
+            "plmnid", "tac1", "downlink_bandwidth", "uplink_bandwidth", "cell_id",
+            "mme_address", "mme_port", "admin_state"
+        ]
+
+        # Configure the additional parameters which are set in enodeb config files
+        for name, value in desired_config.items():
+            if name not in exclude_list:
+                self.set_parameter_if_present(name, value)
diff --git a/devices/baicells.py b/devices/baicells.py
index 7023d34..ed510d9 100644
--- a/devices/baicells.py
+++ b/devices/baicells.py
@@ -153,7 +153,7 @@
         ParameterName.EARFCNDL: TrParam(FAPSERVICE_PATH + 'X_BAICELLS_COM_LTE.EARFCNDLInUse', True, TrParameterType.INT, False),
         ParameterName.EARFCNUL: TrParam(FAPSERVICE_PATH + 'X_BAICELLS_COM_LTE.EARFCNULInUse', True, TrParameterType.INT, False),
         ParameterName.BAND: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.FreqBandIndicator', True, TrParameterType.INT, False),
-        ParameterName.PCI: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', False, TrParameterType.INT, False),
+        ParameterName.PCI1: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', False, TrParameterType.INT, False),
         ParameterName.DL_BANDWIDTH: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.DLBandwidth', True, TrParameterType.STRING, False),
         ParameterName.UL_BANDWIDTH: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.ULBandwidth', True, TrParameterType.STRING, False),
         ParameterName.SUBFRAME_ASSIGNMENT: TrParam(
@@ -182,7 +182,7 @@
         ),
 
         # Core network parameters
-        ParameterName.MME_IP: TrParam(
+        ParameterName.MME_ADDRESS: TrParam(
             FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkServerList', True, TrParameterType.STRING, False,
         ),
         ParameterName.MME_PORT: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkPort', True, TrParameterType.INT, False),
@@ -191,7 +191,7 @@
         ),
         ParameterName.PLMN: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNList.', True, TrParameterType.STRING, False),
         # PLMN arrays are added below
-        ParameterName.TAC: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.INT, False),
+        ParameterName.TAC1: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.INT, False),
         ParameterName.IP_SEC_ENABLE: TrParam(
             DEVICE_PATH + 'Services.FAPService.Ipsec.IPSEC_ENABLE', False, TrParameterType.BOOLEAN, False,
         ),
@@ -301,5 +301,5 @@
 
 
 class BaicellsTrConfigurationInitializer(EnodebConfigurationPostProcessor):
-    def postprocess(self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration) -> None:
+    def postprocess(self, desired_cfg: EnodebConfiguration) -> None:
         desired_cfg.set_parameter(ParameterName.CELL_BARRED, False)
diff --git a/devices/baicells_old.py b/devices/baicells_old.py
index 70c894f..d95cb6d 100644
--- a/devices/baicells_old.py
+++ b/devices/baicells_old.py
@@ -166,7 +166,7 @@
         ParameterName.EARFCNDL: TrParam(FAPSERVICE_PATH + 'X_BAICELLS_COM_LTE.EARFCNDLInUse', True, TrParameterType.INT, False),
         ParameterName.EARFCNUL: TrParam(FAPSERVICE_PATH + 'X_BAICELLS_COM_LTE.EARFCNULInUse', True, TrParameterType.INT, False),
         ParameterName.BAND: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.FreqBandIndicator', True, TrParameterType.INT, False),
-        ParameterName.PCI: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', True, TrParameterType.INT, False),
+        ParameterName.PCI1: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', True, TrParameterType.INT, False),
         ParameterName.DL_BANDWIDTH: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.DLBandwidth', True, TrParameterType.STRING, False),
         ParameterName.UL_BANDWIDTH: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.ULBandwidth', True, TrParameterType.STRING, False),
         ParameterName.SUBFRAME_ASSIGNMENT: TrParam(
@@ -195,7 +195,7 @@
         ),
 
         # Core network parameters
-        ParameterName.MME_IP: TrParam(
+        ParameterName.MME_ADDRESS: TrParam(
             FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkServerList', True, TrParameterType.STRING, False,
         ),
         ParameterName.MME_PORT: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkPort', True, TrParameterType.INT, False),
@@ -204,7 +204,7 @@
         ),
         ParameterName.PLMN: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNList.', True, TrParameterType.STRING, False),
         # PLMN arrays are added below
-        ParameterName.TAC: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.INT, False),
+        ParameterName.TAC1: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.INT, False),
         ParameterName.IP_SEC_ENABLE: TrParam(
             DEVICE_PATH + 'Services.FAPService.Ipsec.IPSEC_ENABLE', False, TrParameterType.BOOLEAN, False,
         ),
@@ -317,5 +317,5 @@
 
 
 class BaicellsTrOldConfigurationInitializer(EnodebConfigurationPostProcessor):
-    def postprocess(self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration) -> None:
+    def postprocess(self, desired_cfg: EnodebConfiguration) -> None:
         desired_cfg.set_parameter(ParameterName.CELL_BARRED, False)
diff --git a/devices/baicells_qafa.py b/devices/baicells_qafa.py
index deb12e4..20d34e4 100644
--- a/devices/baicells_qafa.py
+++ b/devices/baicells_qafa.py
@@ -153,7 +153,7 @@
 
         # RF-related parameters
         ParameterName.EARFCNDL: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.EARFCNDL', True, TrParameterType.INT, False),
-        ParameterName.PCI: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', True, TrParameterType.INT, False),
+        ParameterName.PCI1: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', True, TrParameterType.INT, False),
         ParameterName.DL_BANDWIDTH: TrParam(DEVICE_PATH + 'Services.RfConfig.1.RfCarrierCommon.carrierBwMhz', True, TrParameterType.INT, False),
         ParameterName.SUBFRAME_ASSIGNMENT: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.PHY.TDDFrame.SubFrameAssignment', True, 'bool', False),
         ParameterName.SPECIAL_SUBFRAME_PATTERN: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.PHY.TDDFrame.SpecialSubframePatterns', True, TrParameterType.INT, False),
@@ -165,11 +165,11 @@
         ParameterName.RF_TX_STATUS: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.OpState', True, TrParameterType.BOOLEAN, False),
 
         # Core network parameters
-        ParameterName.MME_IP: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkServerList', True, TrParameterType.STRING, False),
+        ParameterName.MME_ADDRESS: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkServerList', True, TrParameterType.STRING, False),
         ParameterName.MME_PORT: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkPort', True, TrParameterType.INT, False),
         # This parameter is standard but doesn't exist
         # ParameterName.NUM_PLMNS: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNListNumberOfEntries', True, TrParameterType.INT, False),
-        ParameterName.TAC: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.INT, False),
+        ParameterName.TAC1: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.INT, False),
         ParameterName.IP_SEC_ENABLE: TrParam('boardconf.ipsec.ipsecConfig.onBoot', False, TrParameterType.BOOLEAN, False),
 
         # Management server parameters
@@ -258,6 +258,6 @@
 
 
 class BaicellsQAFATrConfigurationInitializer(EnodebConfigurationPostProcessor):
-    def postprocess(self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration) -> None:
+    def postprocess(self, desired_cfg: EnodebConfiguration) -> None:
 
         desired_cfg.delete_parameter(ParameterName.ADMIN_STATE)
diff --git a/devices/baicells_qafb.py b/devices/baicells_qafb.py
index 1cd80df..9714b18 100644
--- a/devices/baicells_qafb.py
+++ b/devices/baicells_qafb.py
@@ -393,7 +393,7 @@
 
         # RF-related parameters
         ParameterName.EARFCNDL: TrParam(FAPSERVICE_PATH + 'CellConfig.1.LTE.RAN.RF.EARFCNDL', True, TrParameterType.INT, False),
-        ParameterName.PCI: TrParam(FAPSERVICE_PATH + 'CellConfig.1.LTE.RAN.RF.PhyCellID', True, TrParameterType.INT, False),
+        ParameterName.PCI1: TrParam(FAPSERVICE_PATH + 'CellConfig.1.LTE.RAN.RF.PhyCellID', True, TrParameterType.INT, False),
         ParameterName.DL_BANDWIDTH: TrParam(DEVICE_PATH + 'Services.RfConfig.1.RfCarrierCommon.carrierBwMhz', True, TrParameterType.INT, False),
         ParameterName.SUBFRAME_ASSIGNMENT: TrParam(FAPSERVICE_PATH + 'CellConfig.1.LTE.RAN.PHY.TDDFrame.SubFrameAssignment', True, 'bool', False),
         ParameterName.SPECIAL_SUBFRAME_PATTERN: TrParam(FAPSERVICE_PATH + 'CellConfig.1.LTE.RAN.PHY.TDDFrame.SpecialSubframePatterns', True, TrParameterType.INT, False),
@@ -405,11 +405,11 @@
         ParameterName.RF_TX_STATUS: TrParam(FAPSERVICE_PATH + 'CellConfig.1.LTE.X_QUALCOMM_FAPControl.OpState', True, TrParameterType.BOOLEAN, False),
 
         # Core network parameters
-        ParameterName.MME_IP: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkServerList', True, TrParameterType.STRING, False),
+        ParameterName.MME_ADDRESS: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkServerList', True, TrParameterType.STRING, False),
         ParameterName.MME_PORT: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkPort', True, TrParameterType.INT, False),
         # This parameter is standard but doesn't exist
         # ParameterName.NUM_PLMNS: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNListNumberOfEntries', True, TrParameterType.INT, False),
-        ParameterName.TAC: TrParam(FAPSERVICE_PATH + 'CellConfig.1.LTE.EPC.TAC', True, TrParameterType.INT, False),
+        ParameterName.TAC1: TrParam(FAPSERVICE_PATH + 'CellConfig.1.LTE.EPC.TAC', True, TrParameterType.INT, False),
         ParameterName.IP_SEC_ENABLE: TrParam('boardconf.ipsec.ipsecConfig.onBoot', False, TrParameterType.BOOLEAN, False),
 
         # Management server parameters
@@ -498,7 +498,7 @@
 
 
 class BaicellsQAFBTrConfigurationInitializer(EnodebConfigurationPostProcessor):
-    def postprocess(self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration) -> None:
+    def postprocess(self, desired_cfg: EnodebConfiguration) -> None:
         # We don't set this parameter for this device, it should be
         # auto-configured by the device.
         desired_cfg.delete_parameter(ParameterName.ADMIN_STATE)
diff --git a/devices/baicells_rts.py b/devices/baicells_rts.py
index ed4899d..1214d6d 100644
--- a/devices/baicells_rts.py
+++ b/devices/baicells_rts.py
@@ -149,7 +149,7 @@
         ParameterName.EARFCNDL: TrParam(FAPSERVICE_PATH + 'X_BAICELLS_COM_LTE.EARFCNDLInUse', True, TrParameterType.INT, False),
         ParameterName.EARFCNUL: TrParam(FAPSERVICE_PATH + 'X_BAICELLS_COM_LTE.EARFCNULInUse', True, TrParameterType.INT, False),
         ParameterName.BAND: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.FreqBandIndicator', True, TrParameterType.INT, False),
-        ParameterName.PCI: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', False, TrParameterType.INT, False),
+        ParameterName.PCI1: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', False, TrParameterType.INT, False),
         ParameterName.DL_BANDWIDTH: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.DLBandwidth', True, TrParameterType.STRING, False),
         ParameterName.UL_BANDWIDTH: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.ULBandwidth', True, TrParameterType.STRING, False),
         ParameterName.SUBFRAME_ASSIGNMENT: TrParam(
@@ -178,7 +178,7 @@
         ),
 
         # Core network parameters
-        ParameterName.MME_IP: TrParam(
+        ParameterName.MME_ADDRESS: TrParam(
             FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkServerList', True, TrParameterType.STRING, False,
         ),
         ParameterName.MME_PORT: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkPort', True, TrParameterType.INT, False),
@@ -187,7 +187,7 @@
         ),
         ParameterName.PLMN: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNList.', True, TrParameterType.STRING, False),
         # PLMN arrays are added below
-        ParameterName.TAC: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.INT, False),
+        ParameterName.TAC1: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.INT, False),
         ParameterName.IP_SEC_ENABLE: TrParam(
             DEVICE_PATH + 'Services.FAPService.Ipsec.IPSEC_ENABLE', False, TrParameterType.BOOLEAN, False,
         ),
@@ -297,5 +297,5 @@
 
 
 class BaicellsRTSTrConfigurationInitializer(EnodebConfigurationPostProcessor):
-    def postprocess(self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration) -> None:
+    def postprocess(self, desired_cfg: EnodebConfiguration) -> None:
         desired_cfg.set_parameter(ParameterName.CELL_BARRED, False)
diff --git a/devices/experimental/cavium.py b/devices/experimental/cavium.py
index 7b939dc..6b9ac56 100644
--- a/devices/experimental/cavium.py
+++ b/devices/experimental/cavium.py
@@ -319,7 +319,7 @@
         ParameterName.EARFCNDL: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.EARFCNDL', True, TrParameterType.UNSIGNED_INT, False),
         ParameterName.EARFCNUL: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.EARFCNUL', True, TrParameterType.UNSIGNED_INT, False),
         ParameterName.BAND: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.FreqBandIndicator', True, TrParameterType.UNSIGNED_INT, False),
-        ParameterName.PCI: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', True, TrParameterType.STRING, False),
+        ParameterName.PCI1: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.PhyCellID', True, TrParameterType.STRING, False),
         ParameterName.DL_BANDWIDTH: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.DLBandwidth', True, TrParameterType.STRING, False),
         ParameterName.UL_BANDWIDTH: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.RF.ULBandwidth', True, TrParameterType.STRING, False),
         ParameterName.CELL_ID: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.RAN.Common.CellIdentity', True, TrParameterType.UNSIGNED_INT, False),
@@ -340,7 +340,7 @@
         ),
 
         # Core network parameters
-        ParameterName.MME_IP: TrParam(
+        ParameterName.MME_ADDRESS: TrParam(
             FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkServerList', True, TrParameterType.STRING, False,
         ),
         ParameterName.MME_PORT: TrParam(FAPSERVICE_PATH + 'FAPControl.LTE.Gateway.S1SigLinkPort', True, TrParameterType.UNSIGNED_INT, False),
@@ -349,7 +349,7 @@
         ),
         ParameterName.PLMN: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNList.', True, TrParameterType.OBJECT, False),
         # PLMN arrays are added below
-        ParameterName.TAC: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.UNSIGNED_INT, False),
+        ParameterName.TAC1: TrParam(FAPSERVICE_PATH + 'CellConfig.LTE.EPC.TAC', True, TrParameterType.UNSIGNED_INT, False),
         ParameterName.IP_SEC_ENABLE: TrParam(
             DEVICE_PATH + 'IPsec.Enable', False, TrParameterType.BOOLEAN, False,
         ),
@@ -478,6 +478,6 @@
 
 
 class CaviumTrConfigurationInitializer(EnodebConfigurationPostProcessor):
-    def postprocess(self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration) -> None:
+    def postprocess(self, desired_cfg: EnodebConfiguration) -> None:
         desired_cfg.set_parameter(ParameterName.CELL_BARRED, True)
         desired_cfg.set_parameter(ParameterName.ADMIN_STATE, True)
diff --git a/devices/freedomfi_one.py b/devices/freedomfi_one.py
index 6149ff6..5b98efd 100644
--- a/devices/freedomfi_one.py
+++ b/devices/freedomfi_one.py
@@ -635,7 +635,7 @@
             type=TrParameterType.STRING,
             is_optional=False,
         ),
-        ParameterName.PCI: TrParam(
+        ParameterName.PCI1: TrParam(
             FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.PhyCellID",
             is_invasive=False,
             type=TrParameterType.STRING,
@@ -673,7 +673,7 @@
             is_optional=False,
         ),
         # Core network parameters
-        ParameterName.MME_IP: TrParam(
+        ParameterName.MME_ADDRESS: TrParam(
             FAP_CONTROL + "LTE.Gateway.S1SigLinkServerList",
             is_invasive=False,
             type=TrParameterType.STRING,
@@ -685,7 +685,7 @@
             type=TrParameterType.INT,
             is_optional=False,
         ),
-        ParameterName.TAC: TrParam(
+        ParameterName.TAC1: TrParam(
             FAPSERVICE_PATH + "CellConfig.LTE.EPC.TAC",
             is_invasive=False,
             type=TrParameterType.INT,
@@ -857,7 +857,7 @@
         self.acs = acs
 
     def postprocess(
-        self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration,
+        self, desired_cfg: EnodebConfiguration,
     ) -> None:
         # Bump up the parameter key version
         self.acs.parameter_version_inc()
diff --git a/devices/sercomm.py b/devices/sercomm.py
index 72df821..a64186a 100644
--- a/devices/sercomm.py
+++ b/devices/sercomm.py
@@ -17,7 +17,6 @@
     ParameterName,
     TrParameterType,
 )
-from configuration.service_configs import load_enb_config
 from device_config.configuration_init import build_desired_config
 from device_config.enodeb_config_postprocessor import EnodebConfigurationPostProcessor
 from device_config.enodeb_configuration import EnodebConfiguration
@@ -495,7 +494,6 @@
 
         pass_through_params = [ParameterName.GPS_LAT, ParameterName.GPS_LONG]
 
-        print(name_to_val)
         for name in pass_through_params:
             device_cfg.set_parameter(name, name_to_val[name])
 
@@ -636,7 +634,7 @@
         ParameterName.DL_BANDWIDTH: TrParam(
             FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.DLBandwidth",
             is_invasive=False,
-            type=TrParameterType.STRING,
+            type=TrParameterType.INT,
             is_optional=False,
         ),
         ParameterName.UL_BANDWIDTH: TrParam(
@@ -645,7 +643,7 @@
             type=TrParameterType.STRING,
             is_optional=False,
         ),
-        ParameterName.PCI: TrParam(
+        ParameterName.PCI_LIST: TrParam(
             FAPSERVICE_PATH + "CellConfig.LTE.RAN.RF.PhyCellID",
             is_invasive=False,
             type=TrParameterType.STRING,
@@ -689,7 +687,7 @@
             is_optional=False,
         ),
         # Core network parameters
-        ParameterName.MME_IP: TrParam(
+        ParameterName.MME_ADDRESS: TrParam(
             FAP_CONTROL + "LTE.Gateway.S1SigLinkServerList",
             is_invasive=False,
             type=TrParameterType.STRING,
@@ -701,7 +699,7 @@
             type=TrParameterType.INT,
             is_optional=False,
         ),
-        ParameterName.TAC: TrParam(
+        ParameterName.TAC1: TrParam(
             FAPSERVICE_PATH + "CellConfig.LTE.EPC.TAC",
             is_invasive=False,
             type=TrParameterType.INT,
@@ -872,23 +870,8 @@
         super().__init__()
         self.acs = acs
 
-    def postprocess(
-        self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration,
-    ) -> None:
-        # Bump up the parameter key version
-        self.acs.parameter_version_inc()
-
-        # Load eNB customized configuration from "./magma_config/serial_number/"
-        # and configure each connected eNB based on serial number
-        enbcfg = load_enb_config()
-        sn = self.acs.get_parameter(ParameterName.SERIAL_NUMBER)
-
-        for name, val in enbcfg.get(sn, {}).items():
-            if desired_cfg.has_parameter(name):
-                desired_cfg.set_parameter(name, val)
-                print("Config %s updated to: %s" % (name, val))
-
-        print(desired_cfg)
+    def postprocess(self, desired_cfg: EnodebConfiguration) -> None:
+        pass
 
 
 class SercommSendGetTransientParametersState(EnodebAcsState):
@@ -1106,11 +1089,7 @@
         # Now we have enough information to build the desired configuration
         if self.acs.desired_cfg is None:
             self.acs.desired_cfg = build_desired_config(
-                self.acs.mconfig,
-                self.acs.service_config,
-                self.acs.device_cfg,
-                self.acs.data_model,
-                self.acs.config_postprocessor,
+                self.acs.device_cfg, self.acs.data_model, self.acs.config_postprocessor,
             )
 
         if (
diff --git a/magma_configs/acs_common.yml b/magma_configs/acs_common.yml
index f757526..dc46cd7 100644
--- a/magma_configs/acs_common.yml
+++ b/magma_configs/acs_common.yml
@@ -1,16 +1,110 @@
-# SPDX-FileCopyrightText: 2020 The Magma Authors.
 # SPDX-FileCopyrightText: 2022 Open Networking Foundation <support@opennetworking.org>
 #
 # SPDX-License-Identifier: BSD-3-Clause
 
-bandwidthMhz: 100
-specialSubframePattern: 7
-earfcndl: 44490
-plmnidList: "00101"
-pci: 1
-allowEnodebTransmit: False
-subframeAssignment: 2
-tac: 1
-cell_id: 1
-mme_address: ""
-mme_port: 36412
+basic:
+  cell_id: 100
+  mme_address: 172.21.143.206
+  mme_port: 36412
+  plmn_list: "305010"
+  pci_list: "100,101"
+  allow_enodeb_transmit: False
+
+  carrier_number: 2
+  carrier_agg_enable: False
+
+# Automation Configuration Server
+acs:
+  # To enable/disable to connect the management server (ACS)
+  cwmp_enable: True
+  # Define to enable/disable a periodic inform
+  periodic_inform_enable: True
+  # Interval of a preiodic inform send to ACS
+  periodic_inform_interval: 180
+
+cell:
+  # If True, 64QAM is allowed [TS 36.331]
+  enable64qam: True
+  # Physical Cell ID for cell 1 and cell 2
+  pci1: 100
+  pci2: 101
+  # Tracking Area Code for cell 1 and cell 2
+  tac1: 501
+  tac2: 502
+  # Downlink/Uplink bandwidth
+  downlink_bandwidth: 25
+  uplink_bandwidth: 25
+  # EARFCN for 1st frequency band
+  earfcn_downlink1: 55440
+  earfcn_uplink1: 55440
+  # EARFCN for 2nd frequency band
+  earfcn_downlink2: 55640
+  earfcn_uplink2: 55640
+  # EARFCN for 1st + 2nd frequency band
+  earfcn_downlink_list: "55440,55640"
+  earfcn_uplink_list: "55440,55640"
+  # First/Second Frequency Band Indicator
+  frequency_band_1: 48
+  frequency_band_2: 48
+  # Frequency Band List
+  frequency_band_list: "48, 48"
+  # The DL/UL subframe configuration
+  subframe_assignment: 2
+  # The TDD special sub-frame pattern [TS 36.331 sec 6.3.2]
+  special_subframe_pattern: 7
+
+  # The real TX power
+  tx_power: 20
+  # Tunnel type
+  tunnel_type: "Device.IP.Interface.1.IPv4Address.1."
+
+
+# Femtocell Access Point Control Parameters
+fap_control:
+  # The administrative state of LTE FAP
+  #  If True, the cell unlock and enable RF to serve traffic
+  admin_state: False
+  # Radio Resource Management
+  is_ca_frequency_contiguous: 0
+
+# Radio Environment Map Parameters
+rem:
+  primary_source: "FREE_RUNNING"
+
+# Spectrum Access System (SAS)
+sas:
+  # If True, eNodeB will be admistrated by SAS
+  sas_enable: True
+  # If True, it indicates device configuration was signed by CPI key
+  cpi_enable: True
+  # If True, add manufacturer name as prefix of Serial Number
+  manufacturer_prefix_enable: True
+  # The SAS server address
+  sas_server_url: "https://sas.goog/v1.2/"
+  # The username registered to SAS
+  sas_uid: "aether"
+  # Define device's SAS category [CFR 96.39(c)]
+  sas_category: "A"
+  # PAL or GAA
+  sas_channel_type: "GAA"
+  # The device's certification name, it uses to communicate with SAS server
+  sas_cert_subject: "ENODEBD_CONFIGURED_SAS_CERT"
+  # Location: indoor / outdoor
+  sas_location: "indoor"
+  # AGL or AMSL
+  sas_height_type: "AGL"
+  # FCC ID of device
+  sas_fccid: "ENODEBD_CONFIGURED_FCCID"
+  # RECEIVED_POWER_WITH_GRANT / RECEIVED_POWER_WITHOUT_GRANT
+  sas_measure_capability: "RECEIVED_POWER_WITHOUT_GRANT"
+  # Your CPI name
+  sas_cpi_name: "ONF_CPI"
+  # Your CPI Certification ID
+  sas_cpi_id: "GOOG-999999"
+  # Device antenna parameters
+  sas_antenna_azimuth: 0
+  sas_antenna_downtilt: 0
+  sas_antenna_gain: 5
+  sas_antenna_beamwidth: 360
+  # The signature of configuration
+  sas_cpi_signature_data: "ENODEBD_CONFIGURED_CPI_SIGNED_DATA"
diff --git a/magma_configs/acs_common_copy.yml b/magma_configs/acs_common_copy.yml
new file mode 100644
index 0000000..f757526
--- /dev/null
+++ b/magma_configs/acs_common_copy.yml
@@ -0,0 +1,16 @@
+# SPDX-FileCopyrightText: 2020 The Magma Authors.
+# SPDX-FileCopyrightText: 2022 Open Networking Foundation <support@opennetworking.org>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+
+bandwidthMhz: 100
+specialSubframePattern: 7
+earfcndl: 44490
+plmnidList: "00101"
+pci: 1
+allowEnodebTransmit: False
+subframeAssignment: 2
+tac: 1
+cell_id: 1
+mme_address: ""
+mme_port: 36412
diff --git a/state_machines/enb_acs_states.py b/state_machines/enb_acs_states.py
index 071cd49..4e72388 100644
--- a/state_machines/enb_acs_states.py
+++ b/state_machines/enb_acs_states.py
@@ -672,8 +672,6 @@
         # Now we can have the desired state
         if self.acs.desired_cfg is None:
             self.acs.desired_cfg = build_desired_config(
-                self.acs.mconfig,
-                self.acs.service_config,
                 self.acs.device_cfg,
                 self.acs.data_model,
                 self.acs.config_postprocessor,
diff --git a/tests/configuration_init_tests.py b/tests/configuration_init_tests.py
index 84e88df..b2f7e74 100644
--- a/tests/configuration_init_tests.py
+++ b/tests/configuration_init_tests.py
@@ -41,7 +41,7 @@
         pci = 3
         _set_pci(self.cfg, pci)
         self.assertEqual(
-            self.cfg.get_parameter(ParameterName.PCI), pci,
+            self.cfg.get_parameter(ParameterName.PCI1), pci,
             'PCI value should be same as what was set',
         )
         with self.assertRaises(ConfigurationError):
@@ -122,7 +122,7 @@
         # Check the ip and port are sort properly
         _set_s1_connection(self.cfg, mme_ip, mme_port)
         self.assertEqual(
-            self.cfg.get_parameter(ParameterName.MME_IP), mme_ip,
+            self.cfg.get_parameter(ParameterName.MME_ADDRESS), mme_ip,
             'Expected mme ip to be set',
         )
         self.assertEqual(