AETHER-2788 Use parameter from configuration to configure eNB

AETHER-2725	CWMP fault returns by eNodeB
AETHER-2692	Check the XML sent by eNodeB and ACS are valid
AETHER-2691	Research on eNodeB TR-069 service issue
AETHER-2788     Use parameter from configuration to configure eNB
AETHER-2789     Load enodeb configuration by the serial number
AETHER-2821     Configure the PLMN which is current not supported by enodebd
AETHER-2839     Create acs_common to own the common attribute of eNodeb configuration
AETHER-2831     Writing documentation of configuring enodebd

This patch contains above jira tickets.
It can work and configure the eNodeB with single configuration now.

Change-Id: I4875d099246a1995de420c4947e7a99823055161
diff --git a/README.md b/README.md
index 61e4a0f..60b0c16 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,20 @@
-# Magma enodebd
+# ENODEBD
 
-## Generate protos
+eNodeB daemon is an Automatic Configuration Server (ACS) which forks from [Facebook Magma project](https://github.com/magma/magma). It currently tested with Sercomm eNodeB P27-SCE4255W small cell.
 
-```bash
-python tools/gen_protos.py proto_files/orc8r/protos proto_files,proto_files/orc8r/protos/prometheus proto_files .
-python tools/gen_protos.py proto_files/lte/protos proto_files,proto_files/orc8r/protos/prometheus proto_files .
-python tools/gen_prometheus_proto.py . .
-```
+# Configuration
+
+We have these configuration files for configuring eNodeBD service.
+
+1. override_configs/gateway.mconfig
+
+The enodebd will generate the empty configuration to config service, and it will load
+`override_configs/gateway.mconfig` to fill in the empty configuration.
+
+2. magma_configs/acs_common.yml
+
+The `acs_common.yml` will hold the default value for all eNodeBs, like as the PLMN may be a shared value among all eNodeBs.
+
+3. magma_configs/serial_numbers/_2009CW5000019_.yml
+
+The `serial_number.yml` will have the customized value for each configurable parameters. The value in `serial_number.yml` will override the value defines in `gateway.mconfig` and `acs_common.yml` when corresponding eNodeB connects (base on the serial number provided by eNodeB).
\ No newline at end of file
diff --git a/common/service.py b/common/service.py
index 2bcce61..5a3ca98 100644
--- a/common/service.py
+++ b/common/service.py
@@ -326,6 +326,7 @@
                 'or mconfig, defaulting to INFO',
             )
             log_level = LogLevel.Value('INFO')
+
         self._set_log_level(log_level)
 
     @staticmethod
diff --git a/configuration/mconfig_managers.py b/configuration/mconfig_managers.py
index bd39c4c..3b76418 100644
--- a/configuration/mconfig_managers.py
+++ b/configuration/mconfig_managers.py
@@ -157,7 +157,7 @@
                 mconfig_str = cfg_file.read()
             return self.deserialize_mconfig(mconfig_str)
         except (OSError, json.JSONDecodeError, json_format.ParseError) as e:
-            raise LoadConfigError('Error loading mconfig') from e
+            raise LoadConfigError('Error loading mconfig, mconfig may have format issue') from e
 
     def load_service_mconfig(
         self, service_name: str,
diff --git a/configuration/service_configs.py b/configuration/service_configs.py
index c60c340..f2d0f24 100644
--- a/configuration/service_configs.py
+++ b/configuration/service_configs.py
@@ -22,7 +22,8 @@
 # Location of configs (both service config and mconfig)
 CONFIG_DIR = './magma_configs'
 CONFIG_OVERRIDE_DIR = './override_configs'
-
+ENB_COMMON_FILE = './magma_configs/acs_common.yml'
+ENB_CONFIG_DIR = './magma_configs/serial_number'
 
 def load_override_config(service_name: str) -> Optional[Any]:
     """
@@ -73,7 +74,6 @@
         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)
 
@@ -83,6 +83,36 @@
         cfg.update(overrides)
     return cfg
 
+def load_enb_config() -> Any:
+    """
+    Load enb configurations from directory.
+
+    Args:
+        None
+
+    Returns: json-decoded value of the service config
+    """
+
+    ret = dict()
+    for fname in os.listdir(ENB_CONFIG_DIR):
+        sn = fname.replace(".yml", "")
+        cfg_file_name = os.path.join(ENB_CONFIG_DIR, fname)
+        ret[sn] = _load_yaml_file(cfg_file_name)
+
+    return ret
+
+def load_common_config() -> Any:
+    """
+    Load enb common configuration.
+
+    Args:
+        None
+
+    Returns: json-decoded value of the service config
+    """
+
+    return _load_yaml_file(ENB_COMMON_FILE)
+
 
 cached_service_configs = {}     # type: Dict[str, Any]
 
diff --git a/device_config/configuration_init.py b/device_config/configuration_init.py
index cf9505b..4b156df 100644
--- a/device_config/configuration_init.py
+++ b/device_config/configuration_init.py
@@ -18,6 +18,7 @@
 from lte.protos.mconfig import mconfigs_pb2
 from common.misc_utils import get_ip_from_if
 from configuration.exceptions import LoadConfigError
+from configuration.service_configs import load_enb_config, load_common_config
 from configuration.mconfig_managers import load_service_mconfig_as_json
 from data_models.data_model import DataModel
 from data_models.data_model_parameters import ParameterName
@@ -78,6 +79,9 @@
     Returns:
         Desired data model configuration for the device
     """
+
+    print("DEVICE CFG: ", device_config)
+
     cfg_desired = EnodebConfiguration(data_model)
 
     # Determine configuration parameters
@@ -150,6 +154,7 @@
         config = json.loads(
             load_service_mconfig_as_json('yang').get('value', '{}'),
         )
+
         enb.extend(
             filter(
                 lambda entry: entry['serial'] == enb_serial,
@@ -185,68 +190,43 @@
         mconfig: mconfigs_pb2.EnodebD,
         device_config: EnodebConfiguration,
 ) -> SingleEnodebConfig:
-    # For fields that are specified per eNB
-    if mconfig.enb_configs_by_serial is not None and \
-            len(mconfig.enb_configs_by_serial) > 0:
-        enb_serial = \
-            device_config.get_parameter(ParameterName.SERIAL_NUMBER)
-        if enb_serial in mconfig.enb_configs_by_serial:
-            enb_config = mconfig.enb_configs_by_serial[enb_serial]
-            earfcndl = enb_config.earfcndl
-            pci = enb_config.pci
-            allow_enodeb_transmit = enb_config.transmit_enabled
-            tac = enb_config.tac
-            bandwidth_mhz = enb_config.bandwidth_mhz
-            cell_id = enb_config.cell_id
-            duplex_mode = map_earfcndl_to_duplex_mode(earfcndl)
-            subframe_assignment = None
-            special_subframe_pattern = None
-            if duplex_mode == DuplexMode.TDD:
-                subframe_assignment = enb_config.subframe_assignment
-                special_subframe_pattern = \
-                    enb_config.special_subframe_pattern
-        else:
-            raise ConfigurationError(
-                'Could not construct desired config '
-                'for eNB',
-            )
-    else:
-        pci = mconfig.pci
-        allow_enodeb_transmit = mconfig.allow_enodeb_transmit
-        tac = mconfig.tac
-        bandwidth_mhz = mconfig.bandwidth_mhz
-        cell_id = DEFAULT_CELL_IDENTITY
-        if mconfig.tdd_config is not None and str(mconfig.tdd_config) != '':
-            earfcndl = mconfig.tdd_config.earfcndl
-            subframe_assignment = mconfig.tdd_config.subframe_assignment
-            special_subframe_pattern = \
-                mconfig.tdd_config.special_subframe_pattern
-        elif mconfig.fdd_config is not None and str(mconfig.fdd_config) != '':
-            earfcndl = mconfig.fdd_config.earfcndl
-            subframe_assignment = None
-            special_subframe_pattern = None
-        else:
-            earfcndl = mconfig.earfcndl
-            subframe_assignment = mconfig.subframe_assignment
-            special_subframe_pattern = mconfig.special_subframe_pattern
 
-    # And now the rest of the fields
-    plmnid_list = mconfig.plmnid_list
+    # 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.
+    # The first parameter is the name of eNB / ACS configuration in
+    #   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 = [
+        ["earfcndl", "earfcndl"],
+        ["subframeAssignment", "subframe_assignment"],
+        ["special_subframe_pattern", "special_subframe_pattern"],
+        ["pci", "pci"],
+        ["plmnidList", "plmnid_list"],
+        ["tac", "tac"],
+        ["bandwidthMhz", "bandwidth_mhz"],
+        ["allowEnodebTransmit", "allow_enodeb_transmit"]
+    ]
+    
+    extend_params = ["cell_id", "mme_address", "mme_port"]
 
-    single_enodeb_config = SingleEnodebConfig(
-        earfcndl=earfcndl,
-        subframe_assignment=subframe_assignment,
-        special_subframe_pattern=special_subframe_pattern,
-        pci=pci,
-        plmnid_list=plmnid_list,
-        tac=tac,
-        bandwidth_mhz=bandwidth_mhz,
-        cell_id=cell_id,
-        allow_enodeb_transmit=allow_enodeb_transmit,
-        mme_address=None,
-        mme_port=None,
-    )
-    return single_enodeb_config
+    params_dict = dict()
+
+    common_config = load_common_config()
+    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[1]] = enb_config.get(param[0], 
+            common_config.get(param[0], mconfig.__getattribute__(param[1]))
+        )
+
+    for param in extend_params:
+        params_dict[param] = enb_config.get(param, common_config.get(param, None))
+
+    return SingleEnodebConfig(**params_dict)
 
 
 def _set_pci(
diff --git a/device_config/enodeb_configuration.py b/device_config/enodeb_configuration.py
index 5d28d50..6e0b951 100644
--- a/device_config/enodeb_configuration.py
+++ b/device_config/enodeb_configuration.py
@@ -158,4 +158,4 @@
         tr_param = trparam_model.get_parameter(param_name)
         if tr_param is None:
             logger.warning('Parameter <%s> not defined in model', param_name)
-            raise ConfigurationError("Parameter not defined in model.")
+            raise ConfigurationError("Parameter %s not defined in model." % param_name)
diff --git a/devices/freedomfi_one.py b/devices/freedomfi_one.py
index e9bf9b0..bfa66eb 100644
--- a/devices/freedomfi_one.py
+++ b/devices/freedomfi_one.py
@@ -24,6 +24,7 @@
     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
@@ -176,7 +177,9 @@
     and converting it to Magma understood fields.
     """
 
-    STATUS_PATH = "Device.X_000E8F_DeviceFeature.X_000E8F_NEStatus."
+    DEFGW_STATUS_PATH = "Device.X_SCM_DeviceFeature.X_SCM_NEStatus.X_SCM_DEFGW_Status"
+    SAS_STATUS_PATH = "Device.Services.FAPService.1.FAPControl.LTE.X_SCM_SAS.State"
+    ENB_STATUS_PATH = "Device.X_SCM_DeviceFeature.X_SCM_NEStatus.X_SCM_eNB_Status"
 
     # Status parameters
     DEFAULT_GW = "defaultGW"
@@ -187,30 +190,29 @@
 
     STATUS_PARAMETERS = {
         # Status nodes
+        # This works
         DEFAULT_GW: TrParam(
-            STATUS_PATH + "X_000E8F_DEFGW_Status",
+            DEFGW_STATUS_PATH,
             is_invasive=False,
             type=TrParameterType.STRING,
             is_optional=False,
         ),
-        SYNC_STATUS: TrParam(
-            STATUS_PATH + "X_000E8F_Sync_Status",
-            is_invasive=False,
-            type=TrParameterType.STRING,
-            is_optional=False,
-        ),
+        # SYNC_STATUS: TrParam(
+        #     STATUS_PATH + 'X_000E8F_Sync_Status', is_invasive=False,
+        #     type=TrParameterType.STRING, is_optional=False,
+        # ),
+        # This works
         SAS_STATUS: TrParam(
-            STATUS_PATH + "X_000E8F_SAS_Status",
+            SAS_STATUS_PATH,
             is_invasive=False,
             type=TrParameterType.STRING,
             is_optional=False,
         ),
-        ENB_STATUS: TrParam(
-            STATUS_PATH + "X_000E8F_eNB_Status",
-            is_invasive=False,
-            type=TrParameterType.STRING,
-            is_optional=False,
-        ),
+        # This doesn't work
+        # ENB_STATUS: TrParam(
+        #     ENB_STATUS_PATH, is_invasive=False,
+        #     type=TrParameterType.STRING, is_optional=False,
+        # ),
         # GPS status, lat, long
         GPS_SCAN_STATUS: TrParam(
             "Device.FAP.GPS.ScanStatus",
@@ -397,12 +399,11 @@
     WEB_UI_ENABLE = "web_ui_enable"  # Enable or disable local enb UI
 
     MISC_PARAMETERS = {
-        WEB_UI_ENABLE: TrParam(
-            "Device.X_000E8F_DeviceFeature.X_000E8F_WebServerEnable",
-            is_invasive=False,
-            type=TrParameterType.BOOLEAN,
-            is_optional=False,
-        ),
+        # WEB_UI_ENABLE: TrParam(
+        #     'Device.X_000E8F_DeviceFeature.X_000E8F_WebServerEnable',
+        #     is_invasive=False,
+        #     type=TrParameterType.BOOLEAN, is_optional=False,
+        # ),
         CARRIER_AGG_ENABLE: TrParam(
             FAP_CONTROL + "LTE.X_000E8F_RRMConfig.X_000E8F_CA_Enable",
             is_invasive=False,
@@ -440,12 +441,11 @@
         # Use IPV4 only
         TUNNEL_REF: "Device.IP.Interface.1.IPv4Address.1.",
         # Only synchronize with GPS
-        PRIM_SOURCE: "GNSS",
+        PRIM_SOURCE: "FREE_RUNNING",
         # Always enable carrier aggregation for the CBRS bands
-        CARRIER_AGG_ENABLE: True,
-        CARRIER_NUMBER: 2,  # CBRS has two carriers
+        CARRIER_AGG_ENABLE: False,
+        CARRIER_NUMBER: 1,  # CBRS has two carriers
         CONTIGUOUS_CC: 0,  # Its not contiguous carrier
-        WEB_UI_ENABLE: False,  # Disable WebUI by default
     }
 
 
@@ -657,12 +657,12 @@
             type=TrParameterType.INT,
             is_optional=False,
         ),
-        ParameterName.NUM_PLMNS: TrParam(
-            FAPSERVICE_PATH + "CellConfig.LTE.EPC.PLMNListNumberOfEntries",
-            is_invasive=False,
-            type=TrParameterType.INT,
-            is_optional=False,
-        ),
+        # It may not work, comment out first
+        # ParameterName.NUM_PLMNS: TrParam(
+        #     FAPSERVICE_PATH + 'CellConfig.LTE.EPC.PLMNListNumberOfEntries',
+        #     is_invasive=False,
+        #     type=TrParameterType.INT, is_optional=False,
+        # ),
         ParameterName.TAC: TrParam(
             FAPSERVICE_PATH + "CellConfig.LTE.EPC.TAC",
             is_invasive=False,
@@ -821,10 +821,7 @@
     def postprocess(
         self, mconfig: Any, service_cfg: Any, desired_cfg: EnodebConfiguration,
     ) -> None:
-        # TODO: Get this config from the domain proxy
-        # TODO @amarpad, set these when DProxy integration is done.
-        # For now the radio will directly talk to the SAS and get these
-        # attributes.
+
         desired_cfg.delete_parameter(ParameterName.EARFCNDL)
         desired_cfg.delete_parameter(ParameterName.DL_BANDWIDTH)
         desired_cfg.delete_parameter(ParameterName.UL_BANDWIDTH)
@@ -847,16 +844,19 @@
                 # This should not happen
                 EnodebdLogger.error("Serial number unknown for device")
 
-        if self.SAS_KEY not in service_cfg:
-            return
+        # 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)
 
-        sas_cfg = service_cfg[self.SAS_KEY]
-        sas_param_names = self.acs.data_model.get_sas_param_names()
-        for name, val in sas_cfg.items():
-            if name not in sas_param_names:
-                EnodebdLogger.warning("Ignoring attribute %s", name)
-                continue
-            desired_cfg.set_parameter(name, val)
+        for name, val in enbcfg.get(sn, {}).items():
+            # The SAS configuration for eNodeB
+            if name in ["sas", "cell"]:
+                for subname, subval in val.items():
+                    print("Config %s updated to: %s" % (subname, subval))
+                    desired_cfg.set_parameter(subname, subval)
+
+        print(desired_cfg)
 
 
 class FreedomFiOneSendGetTransientParametersState(EnodebAcsState):
@@ -872,12 +872,20 @@
         self.done_transition = when_done
 
     def get_msg(self, message: Any) -> AcsMsgAndTransition:
+
         request = models.GetParameterValues()
         request.ParameterNames = models.ParameterNames()
         request.ParameterNames.string = []
+
+        # request = models.GetParameterNames()
+        # request.ParameterPath = "Device."
+        # request.NextLevel = False
+
+        # Get the status parameters which was defined in Line 171
         for _, tr_param in StatusParameters.STATUS_PARAMETERS.items():
             path = tr_param.path
             request.ParameterNames.string.append(path)
+
         request.ParameterNames.arrayType = "xsd:string[%d]" % len(
             request.ParameterNames.string
         )
@@ -1031,6 +1039,7 @@
 
         # Parse simple params
         param_name_list = self.acs.data_model.get_parameter_names()
+
         for name in param_name_list:
             path = self.acs.data_model.get_parameter(name).path
             if path in path_to_val:
@@ -1056,6 +1065,7 @@
                     self.acs.device_cfg.set_parameter_for_object(
                         name, magma_value, obj_name,
                     )
+
         # Now we have enough information to build the desired configuration
         if self.acs.desired_cfg is None:
             self.acs.desired_cfg = build_desired_config(
diff --git a/magma_configs/acs_common.yml b/magma_configs/acs_common.yml
new file mode 100644
index 0000000..28f630b
--- /dev/null
+++ b/magma_configs/acs_common.yml
@@ -0,0 +1,11 @@
+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/magma_configs/enodebd.yml b/magma_configs/enodebd.yml
index 84e636f..ff5fe6e 100644
--- a/magma_configs/enodebd.yml
+++ b/magma_configs/enodebd.yml
@@ -14,26 +14,26 @@
 # log_level is set in mconfig. It can be overridden here
 
 tr069:
-  interface: en0 # NOTE: this value must be consistent with dnsmasq.conf
+  interface: eth0 # NOTE: this value must be consistent with dnsmasq.conf
   port: 48080
   perf_mgmt_port: 8081
   # NOTE: this is the IP which enodeb will communicate with enodebd
   #       if this is ever changed in dnsd.yml, this needs to be updated too
-  public_ip: 192.88.99.142
+  public_ip: 18.116.99.179
 
 # TODO: @amar: This is a temp workaround to allow for testing until we
 # connect enodebd with the domain proxy which is responsible for talking to
 # SAS.
 sas:
   sas_enabled: True
-  sas_server_url: "https://spectrum-connect.federatedwireless.com/v1.2/"
-  sas_uid: "INVALID_ID"
+  sas_server_url: "https://sas.goog/v1.2/"
+  sas_uid: "aether"
   sas_category: "A"
   sas_channel_type: "GAA"
-  sas_cert_subject: "INVALID_CERT_SUBJECT"
+  sas_cert_subject: "/C=TW/O=Sercomm/OU=WInnForum CBSD Certificate/CN=P27-SCE4255W:2009CW5000019"
   sas_icg_group_id: ""
   sas_location: "indoor"
-  sas_height_type: "AMSL"
+  sas_height_type: "AGL"
 
 # Reboot eNodeB if eNodeB should be connected to MME but isn't
 # This is a workaround for a bug with BaiCells eNodeB where the S1 connection
diff --git a/magma_configs/serial_number/2009CW5000019.yml b/magma_configs/serial_number/2009CW5000019.yml
new file mode 100644
index 0000000..0b0e52f
--- /dev/null
+++ b/magma_configs/serial_number/2009CW5000019.yml
@@ -0,0 +1,29 @@
+bandwidthMhz: 100
+specialSubframePattern: 7
+earfcndl: 44490
+plmnidList: "305010"
+pci: 501
+allowEnodebTransmit: False
+subframeAssignment: 2
+tac: 2
+cell_id: 1
+mme_address: 172.21.143.206
+mme_port: 36412
+
+cell:
+  DL bandwidth: 100
+  UL bandwidth: 100
+  Admin state: 0
+  Periodic inform interval: 180
+  Perf mgmt enable: 0
+  Perf mgmt upload interval: 900
+
+sas:
+  sas_enabled: True
+  sas_server_url: "https://sas.goog/v1.2/"
+  sas_uid: "aether"
+  sas_category: "A"
+  sas_channel_type: "GAA"
+  sas_cert_subject: "/C=TW/O=Sercomm/OU=WInnForum CBSD Certificate/CN=P27-SCE4255W:2009CW5000019"
+  sas_location: "indoor"
+  sas_height_type: "AGL"
\ No newline at end of file
diff --git a/override_configs/gateway.mconfig b/override_configs/gateway.mconfig
index 4fbc7dd..c330502 100644
--- a/override_configs/gateway.mconfig
+++ b/override_configs/gateway.mconfig
@@ -6,12 +6,11 @@
       "specialSubframePattern": 7,
       "earfcndl": 44490,
       "logLevel": "INFO",
-      "plmnidList": "00101",
+      "plmnidList": "305011",
       "pci": 260,
       "allowEnodebTransmit": false,
       "subframeAssignment": 2,
-      "tac": 1,
-      "enb_configs_by_serial": {"2009CW5000019": {}}
+      "tac": 1
     }
   }
-}
\ No newline at end of file
+}
diff --git a/state_machines/enb_acs_impl.py b/state_machines/enb_acs_impl.py
index c29690e..2a7367f 100644
--- a/state_machines/enb_acs_impl.py
+++ b/state_machines/enb_acs_impl.py
@@ -65,6 +65,7 @@
         self.mme_timeout_handler = None
         self.mme_timer = None
         self._start_state_machine(service)
+        
 
     def get_state(self) -> str:
         if self.state is None:
diff --git a/tr069/rpc_methods.py b/tr069/rpc_methods.py
index aff0f30..c6203d9 100644
--- a/tr069/rpc_methods.py
+++ b/tr069/rpc_methods.py
@@ -148,6 +148,7 @@
                 # Allow un-set parameters. If CPE can't handle this, it will
                 # respond with an error message
                 pass
+
         return request_out
 
     # CPE->ACS RPC calls
@@ -327,6 +328,7 @@
     # would pick up all tags that start with the tag of interest (e.g
     # cwmp:SetParameterAttributes would also match
     # cwmp:SetParameterAttributesStruct)
+
     XML_FORMAT_STRS = [
         ["cwmp:%s>", "!!!TEMP_MOD!!!:%s>"],
         ["cwmp:%s/>", "!!!TEMP_MOD!!!:%s/>"],
@@ -364,7 +366,6 @@
     if(ctx.descriptor.out_message.Attributes.sub_name == 'EmptyHttp'):
         ctx.out_string = [b'']
 
-
 AutoConfigServer.event_manager.add_listener(
     'method_return_string',
     on_method_return_string,
diff --git a/tr069/spyne_mods.py b/tr069/spyne_mods.py
index 0f0e918..ad0b54b 100644
--- a/tr069/spyne_mods.py
+++ b/tr069/spyne_mods.py
@@ -91,11 +91,11 @@
         ctx.in_string = [in_string.encode(charset, 'ignore')]
         if ctx.in_string == [b'']:
             ctx.in_string = [
-                b'<soap11env:Envelope xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap11env="http://schemas.xmlsoap.org/soap/envelope/">/n'
-                b'   <soap11env:Body>/n'
+                b'<soapenv:Envelope xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">/n'
+                b'   <soapenv:Body>/n'
                 b'       <cwmp:EmptyHttp/>/n'
-                b'   </soap11env:Body>/n'
-                b'</soap11env:Envelope>',
+                b'   </soapenv:Body>/n'
+                b'</soapenv:Envelope>',
             ]
 
         super(Tr069Soap11, self).create_in_document(ctx, charset)