[CORD-2938] Moving convenience methods in the synchronizer containers

Change-Id: I3b189006f43e3aae89e50b7802306da86b339a22
diff --git a/xos/coreapi/dynamicbuild.py b/xos/coreapi/dynamicbuild.py
index 96e4d39..b35fcab 100644
--- a/xos/coreapi/dynamicbuild.py
+++ b/xos/coreapi/dynamicbuild.py
@@ -36,6 +36,7 @@
         self.coreapi_dir = os.path.join(base_dir, "coreapi")
         self.protos_dir = os.path.join(base_dir, "coreapi/protos")
         self.app_metadata_dir = os.path.join(base_dir, "xos")
+        self.convenience_methods_dir = os.path.join(base_dir, "xos_client/xosapi/convenience")
 
     def pre_validate_file(self, item):
         # someone might be trying to trick us into writing files outside the designated directory
@@ -174,7 +175,8 @@
                             "dest_dir": os.path.join(self.services_dest_dir, request.name),
                             "xprotos": [],
                             "decls": [],
-                            "attics": []}
+                            "attics": [],
+                            "convenience_methods": []}
 
         if (state == "load"):
             for item in request.xprotos:
@@ -194,6 +196,14 @@
                     file(os.path.join(attic_dir, item.filename), "w").write(item.contents)
                     service_manifest["attics"].append({"filename": item.filename})
 
+            for item in request.convenience_methods:
+                save_path = os.path.join(self.convenience_methods_dir, item.filename)
+                file(save_path, "w").write(item.contents)
+                service_manifest["convenience_methods"].append({
+                    "filename": item.filename,
+                    "path": save_path
+                })
+
         return service_manifest
 
     def run_xosgenx_service(self, manifest):
diff --git a/xos/coreapi/protos/dynamicload.proto b/xos/coreapi/protos/dynamicload.proto
index 353804f..0279f71 100644
--- a/xos/coreapi/protos/dynamicload.proto
+++ b/xos/coreapi/protos/dynamicload.proto
@@ -20,14 +20,24 @@
     string contents = 2;
 };
 
+message APIConvenienceFile {
+    string filename = 1;
+    string contents = 2;
+};
+
 message LoadModelsRequest {
     string name = 1;
     string version = 2;
     repeated Xproto xprotos = 3;
     repeated DeclFile decls = 4;
     repeated AtticFile attics = 5;
+    repeated APIConvenienceFile convenience_methods = 6;
 };
 
+message ListConvenienceMethodsReply {
+    repeated APIConvenienceFile convenience_methods = 1;
+}
+
 message LoadModelsReply {
     enum LoadModelsStatus {
         SUCCESS = 0;
@@ -71,4 +81,10 @@
             get: "/xosapi/v1/dynamicload/load_status"
         };
   }
+  rpc GetConvenienceMethods(google.protobuf.Empty) returns (ListConvenienceMethodsReply) {
+        option (googleapi.http) = {
+            // NOTE do we need to expose this via rest? maybe for debug...
+            get: "/xosapi/v1/dynamicload/convenience_methods"
+        };
+  }
 };
diff --git a/xos/coreapi/xos_dynamicload_api.py b/xos/coreapi/xos_dynamicload_api.py
index 2d775dc..30ef79b 100644
--- a/xos/coreapi/xos_dynamicload_api.py
+++ b/xos/coreapi/xos_dynamicload_api.py
@@ -28,6 +28,8 @@
 
 from xosutil.autodiscover_version import autodiscover_version_of_main
 from dynamicbuild import DynamicBuilder
+# NOTE/FIXME this file is loaded before Django, we can't import apihelper
+# from apihelper import XOSAPIHelperMixin, translate_exceptions
 
 class DynamicLoadService(dynamicload_pb2_grpc.dynamicloadServicer):
     def __init__(self, thread_pool, server):
@@ -115,3 +117,24 @@
         except Exception, e:
             import traceback; traceback.print_exc()
             raise e
+
+    def GetConvenienceMethods(self, request, context):
+        # self.authenticate(context, required=True)
+        try:
+            builder = DynamicBuilder()
+            manifests = builder.get_manifests()
+
+            response = dynamicload_pb2.ListConvenienceMethodsReply()
+
+            for manifest in manifests:
+                for cm in manifest["convenience_methods"]:
+                    item = response.convenience_methods.add()
+                    item.filename = cm["filename"]
+                    item.contents = open(cm["path"]).read()
+            return response
+
+        except Exception, e:
+            import traceback; traceback.print_exc()
+            raise e
+
+
diff --git a/xos/synchronizers/new_base/loadmodels.py b/xos/synchronizers/new_base/loadmodels.py
index b9cb1f9..dc8be4b 100644
--- a/xos/synchronizers/new_base/loadmodels.py
+++ b/xos/synchronizers/new_base/loadmodels.py
@@ -14,6 +14,10 @@
 # limitations under the License.
 
 import os
+from xosconfig import Config
+from multistructlog import create_logger
+
+log = create_logger(Config().get('logging'))
 
 class ModelLoadClient(object):
     def __init__(self, api):
@@ -36,11 +40,20 @@
 
         attic_dir = os.path.join(dir, "attic")
         if os.path.exists(attic_dir):
+            log.warn("Attics are deprecated, please use the legacy=True option in xProto")
             for fn in os.listdir(attic_dir):
                 if fn.endswith(".py"):
                     item = request.attics.add()
                     item.filename = fn
                     item.contents = open(os.path.join(attic_dir, fn)).read()
 
+        api_convenience_dir = os.path.join(dir, "convenience")
+        if os.path.exists(api_convenience_dir):
+            for fn in os.listdir(api_convenience_dir):
+                if fn.endswith(".py") and not "test" in fn:
+                    item = request.convenience_methods.add()
+                    item.filename = fn
+                    item.contents = open(os.path.join(api_convenience_dir, fn)).read()
+
         result = self.api.dynamicload.LoadModels(request)
 
diff --git a/xos/synchronizers/new_base/modelaccessor.py b/xos/synchronizers/new_base/modelaccessor.py
index 6730879..2167ca5 100644
--- a/xos/synchronizers/new_base/modelaccessor.py
+++ b/xos/synchronizers/new_base/modelaccessor.py
@@ -30,7 +30,6 @@
 import signal
 import sys
 import time
-from xosconfig import Config
 from diag import update_diag
 from loadmodels import ModelLoadClient
 
@@ -231,7 +230,6 @@
     # Restore the sigint handler
     signal.signal(signal.SIGINT, orig_sigint)
 
-
 def config_accessor_grpcapi():
     global orig_sigint
 
diff --git a/xos/xos_client/xosapi/convenience/addressmanagerservice.py b/xos/xos_client/xosapi/convenience/addressmanagerservice.py
deleted file mode 100644
index 6c59bec..0000000
--- a/xos/xos_client/xosapi/convenience/addressmanagerservice.py
+++ /dev/null
@@ -1,31 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# 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 json
-from xosapi.orm import ORMWrapper, register_convenience_wrapper
-from xosapi.convenience.service import ORMWrapperService
-
-class ORMWrapperAddressManagerService(ORMWrapperService):
-    def get_gateways(self):
-        gateways = []
-
-        aps = self.addresspools.all()
-        for ap in aps:
-            gateways.append({"gateway_ip": ap.gateway_ip, "gateway_mac": ap.gateway_mac})
-
-        return gateways
-
-register_convenience_wrapper("AddressManagerService", ORMWrapperAddressManagerService)
diff --git a/xos/xos_client/xosapi/convenience/addressmanagerserviceinstance.py b/xos/xos_client/xosapi/convenience/addressmanagerserviceinstance.py
deleted file mode 100644
index cfb7f69..0000000
--- a/xos/xos_client/xosapi/convenience/addressmanagerserviceinstance.py
+++ /dev/null
@@ -1,67 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# 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 json
-from xosapi.orm import ORMWrapper, register_convenience_wrapper
-
-class ORMWrapperAddressManagerServiceInstance(ORMWrapper):
-    @property
-    def gateway_ip(self):
-        if not self.address_pool:
-            return None
-        return self.address_pool.gateway_ip
-
-    @property
-    def gateway_mac(self):
-        if not self.address_pool:
-            return None
-        return self.address_pool.gateway_mac
-
-    @property
-    def cidr(self):
-        if not self.address_pool:
-            return None
-        return self.address_pool.cidr
-
-    @property
-    def netbits(self):
-        # return number of bits in the network portion of the cidr
-        if self.cidr:
-            parts = self.cidr.split("/")
-            if len(parts) == 2:
-                return int(parts[1].strip())
-        return None
-
-    # Use for tenant_for_instance_id
-    # TODO: These should be reimplemented using real database models
-
-    def get_attribute(self, name, default=None):
-        if self.service_specific_attribute:
-            attributes = json.loads(self.service_specific_attribute)
-        else:
-            attributes = {}
-        return attributes.get(name, default)
-
-    def set_attribute(self, name, value):
-        if self.service_specific_attribute:
-            attributes = json.loads(self.service_specific_attribute)
-        else:
-            attributes = {}
-        attributes[name] = value
-        self.service_specific_attribute = json.dumps(attributes)
-
-
-register_convenience_wrapper("AddressManagerServiceInstance", ORMWrapperAddressManagerServiceInstance)
diff --git a/xos/xos_client/xosapi/convenience/cordsubscriberroot.py b/xos/xos_client/xosapi/convenience/cordsubscriberroot.py
deleted file mode 100644
index 5af889c..0000000
--- a/xos/xos_client/xosapi/convenience/cordsubscriberroot.py
+++ /dev/null
@@ -1,55 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# 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 json
-from xosapi.orm import ORMWrapper, register_convenience_wrapper
-
-class ORMWrapperCordSubscriberRoot(ORMWrapper):
-    @property
-    def volt(self):
-        links = self.subscribed_links.all()
-        for link in links:
-            # TODO: hardcoded service dependency
-            # cast from ServiceInstance to VOLTServiceInstance
-            volts = self.stub.VOLTServiceInstance.objects.filter(id = link.provider_service_instance.id)
-            if volts:
-                return volts[0]
-        return None
-
-    sync_attributes = ("firewall_enable",
-                       "firewall_rules",
-                       "url_filter_enable",
-                       "url_filter_rules",
-                       "cdn_enable",
-                       "uplink_speed",
-                       "downlink_speed",
-                       "enable_uverse",
-                       "status")
-
-    # figure out what to do about "devices"... is it still needed?
-
-    def get_attribute(self, name, default=None):
-        if self.service_specific_attribute:
-            attributes = json.loads(self.service_specific_attribute)
-        else:
-            attributes = {}
-        return attributes.get(name, default)
-
-    @property
-    def devices(self):
-        return self.get_attribute("devices", [])
-
-register_convenience_wrapper("CordSubscriberRoot", ORMWrapperCordSubscriberRoot)
diff --git a/xos/xos_client/xosapi/convenience/voltserviceinstance.py b/xos/xos_client/xosapi/convenience/voltserviceinstance.py
deleted file mode 100644
index 158d1ce..0000000
--- a/xos/xos_client/xosapi/convenience/voltserviceinstance.py
+++ /dev/null
@@ -1,121 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# 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 xosapi.orm import ORMWrapper, register_convenience_wrapper
-
-import logging as log
-
-class ORMWrapperVOLTServiceInstance(ORMWrapper):
-
-    @property
-    def vsg(self):
-        log.warning('VOLTServiceInstance.vsg is DEPRECATED, use get_westbound_service_instance_properties instead')
-        links = self.stub.ServiceInstanceLink.objects.filter(subscriber_service_instance_id = self.id)
-        for link in links:
-            # cast from ServiceInstance to VSGTenant
-            vsgs = self.stub.VSGServiceInstance.objects.filter(id = link.provider_service_instance.id)
-            if vsgs:
-                return vsgs[0]
-        return None
-
-    # DEPRECATED
-    @property
-    def vcpe(self):
-        log.warning('VOLTServiceInstance.vcpe is DEPRECATED, use VOLTServiceInstance.vsg instead')
-        return self.vsg
-
-    @property
-    def subscriber(self):
-        log.warning(
-            'VOLTServiceInstance.subscriber is DEPRECATED, use get_westbound_service_instance_properties instead')
-        # NOTE this assume that each VOLT has just 1 subscriber, is that right?
-        links = self.stub.ServiceInstanceLink.objects.filter(provider_service_instance_id = self.id)
-        for link in links:
-            subs = self.stub.CordSubscriberRoot.objects.filter(id=link.subscriber_service_instance_id)
-            if subs:
-                return subs[0]
-        return None
-
-    @property
-    def c_tag(self):
-        log.warning(
-            'VOLTServiceInstance.c_tag is DEPRECATED, use get_westbound_service_instance_properties instead')
-        return self.subscriber.c_tag
-
-    def get_olt_device_by_subscriber(self):
-        si = self.stub.ServiceInstance.objects.get(id=self.id)
-
-        olt_device_name = si.get_westbound_service_instance_properties("olt_device")
-
-        olt_device = self.stub.OLTDevice.objects.get(name=olt_device_name)
-        return olt_device
-
-    def get_olt_port_by_subscriber(self):
-        si = self.stub.ServiceInstance.objects.get(id=self.id)
-
-        olt_port_name = si.get_westbound_service_instance_properties("olt_port")
-
-        olt_device = self.get_olt_device_by_subscriber()
-        olt_port = self.stub.PONPort.objects.get(name=olt_port_name, olt_device_id=olt_device.id)
-        return olt_port
-
-    @property
-    def s_tag(self):
-        try:
-            olt_port = self.get_olt_port_by_subscriber()
-
-            if olt_port:
-                return olt_port.s_tag
-            return None
-        except Exception, e:
-            log.warning('Error while reading s_tag: %s' % e.message)
-            return None
-
-    @property
-    def switch_datapath_id(self):
-        try:
-            olt_device = self.get_olt_device_by_subscriber()
-            if olt_device:
-                return olt_device.switch_datapath_id
-            return None
-        except Exception, e:
-            log.warning('Error while reading switch_datapath_id: %s' % e.message)
-            return None
-
-    @property
-    def switch_port(self):
-        try:
-            olt_device = self.get_olt_device_by_subscriber()
-            if olt_device:
-                return olt_device.switch_port
-            return None
-        except Exception, e:
-            log.warning('Error while reading switch_port: %s' % e.message)
-            return None
-
-    @property
-    def outer_tpid(self):
-        try:
-            olt_device = self.get_olt_device_by_subscriber()
-            if olt_device:
-                return olt_device.outer_tpid
-            return None
-        except Exception, e:
-            log.warning('Error while reading outer_tpid: %s' % e.message)
-            return None
-
-
-register_convenience_wrapper("VOLTServiceInstance", ORMWrapperVOLTServiceInstance)
diff --git a/xos/xos_client/xosapi/convenience/vrouterapp.py b/xos/xos_client/xosapi/convenience/vrouterapp.py
deleted file mode 100644
index 3dafde2..0000000
--- a/xos/xos_client/xosapi/convenience/vrouterapp.py
+++ /dev/null
@@ -1,33 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# 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 json
-from xosapi.orm import ORMWrapper, register_convenience_wrapper
-
-class ORMWrapperVRouterApp(ORMWrapper):
-    @property
-    def interfaces(self):
-        app_interfaces = []
-        devices = self.stub.VRouterDevice.objects.filter(vrouter_service_id=self.vrouter_service.id)
-        for device in devices:
-            ports = self.stub.VRouterPort.objects.filter(vrouter_device_id=device.id)
-            for port in ports:
-                interfaces = self.stub.VRouterInterface.objects.filter(vrouter_port_id=port.id)
-                for iface in interfaces:
-                    app_interfaces.append(iface.name)
-        return app_interfaces
-
-register_convenience_wrapper("VRouterApp", ORMWrapperVRouterApp)
diff --git a/xos/xos_client/xosapi/convenience/vrouterservice.py b/xos/xos_client/xosapi/convenience/vrouterservice.py
deleted file mode 100644
index 28f1421..0000000
--- a/xos/xos_client/xosapi/convenience/vrouterservice.py
+++ /dev/null
@@ -1,31 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# 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 json
-from xosapi.orm import ORMWrapper, register_convenience_wrapper
-from xosapi.convenience.service import ORMWrapperService
-
-class ORMWrapperVRouterService(ORMWrapperService):
-    def get_gateways(self):
-        gateways = []
-
-        aps = self.addresspools.all()
-        for ap in aps:
-            gateways.append({"gateway_ip": ap.gateway_ip, "gateway_mac": ap.gateway_mac})
-
-        return gateways
-
-register_convenience_wrapper("VRouterService", ORMWrapperVRouterService)
diff --git a/xos/xos_client/xosapi/convenience/vroutertenant.py b/xos/xos_client/xosapi/convenience/vroutertenant.py
deleted file mode 100644
index f9b76c2..0000000
--- a/xos/xos_client/xosapi/convenience/vroutertenant.py
+++ /dev/null
@@ -1,67 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# 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 json
-from xosapi.orm import ORMWrapper, register_convenience_wrapper
-
-class ORMWrapperVRouterTenant(ORMWrapper):
-    @property
-    def gateway_ip(self):
-        if not self.address_pool:
-            return None
-        return self.address_pool.gateway_ip
-
-    @property
-    def gateway_mac(self):
-        if not self.address_pool:
-            return None
-        return self.address_pool.gateway_mac
-
-    @property
-    def cidr(self):
-        if not self.address_pool:
-            return None
-        return self.address_pool.cidr
-
-    @property
-    def netbits(self):
-        # return number of bits in the network portion of the cidr
-        if self.cidr:
-            parts = self.cidr.split("/")
-            if len(parts) == 2:
-                return int(parts[1].strip())
-        return None
-
-    # Use for tenant_for_instance_id
-    # TODO: These should be reimplemented using real database models
-
-    def get_attribute(self, name, default=None):
-        if self.service_specific_attribute:
-            attributes = json.loads(self.service_specific_attribute)
-        else:
-            attributes = {}
-        return attributes.get(name, default)
-
-    def set_attribute(self, name, value):
-        if self.service_specific_attribute:
-            attributes = json.loads(self.service_specific_attribute)
-        else:
-            attributes = {}
-        attributes[name] = value
-        self.service_specific_attribute = json.dumps(attributes)
-
-
-register_convenience_wrapper("VRouterTenant", ORMWrapperVRouterTenant)
diff --git a/xos/xos_client/xosapi/convenience/vsgserviceinstance.py b/xos/xos_client/xosapi/convenience/vsgserviceinstance.py
deleted file mode 100644
index 800ad53..0000000
--- a/xos/xos_client/xosapi/convenience/vsgserviceinstance.py
+++ /dev/null
@@ -1,108 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# 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 xosapi.orm import ORMWrapper, register_convenience_wrapper
-
-class ORMWrapperVSGServiceInstance(ORMWrapper):
-    sync_attributes = ("wan_container_ip", "wan_container_mac", "wan_container_netbits",
-                       "wan_container_gateway_ip", "wan_container_gateway_mac",
-                       "wan_vm_ip", "wan_vm_mac")
-
-    @property
-    def ingress_service_instance(self):
-        links = self.provided_links.all()
-        for link in links:
-            subscriber_service_instance = link.subscriber_service_instance.leaf_model
-            # Look for something that has an s_tag attribute
-            if (hasattr(subscriber_service_instance, "s_tag")):
-                return subscriber_service_instance
-        return None
-
-    @property
-    def volt(self):
-        return self.ingress_service_instance
-
-    def is_address_manager_service_instance(self, si):
-        # TODO: hardcoded dependency
-        # TODO: VRouterTenant is deprecated
-        return si.leaf_model_name in ["AddressManagerServiceInstance", "VRouterTenant"]
-
-    # DEPRECATED
-    @property
-    def vrouter(self):
-        return self.address_service_instance
-
-    @property
-    def address_service_instance(self):
-        links = self.subscribed_links.all()
-        for link in links:
-            if not self.is_address_manager_service_instance(link.provider_service_instance):
-                continue
-            # cast from ServiceInstance to AddressManagerServiceInstance or similar
-            return link.provider_service_instance.leaf_model
-        return None
-
-    def get_address_service_instance_field(self, name, default=None):
-        if self.address_service_instance:
-            return getattr(self.address_service_instance, name, default)
-        else:
-            return default
-
-    @property
-    def wan_container_ip(self):
-        return self.get_address_service_instance_field("public_ip", None)
-
-    @property
-    def wan_container_mac(self):
-        return self.get_address_service_instance_field("public_mac", None)
-
-    @property
-    def wan_container_netbits(self):
-        return self.get_address_service_instance_field("netbits", None)
-
-    @property
-    def wan_container_gateway_ip(self):
-        return self.get_address_service_instance_field("gateway_ip", None)
-
-    @property
-    def wan_container_gateway_mac(self):
-        return self.get_address_service_instance_field("gateway_mac", None)
-
-    @property
-    def wan_vm_ip(self):
-        tags = self.stub.Tag.objects.filter(name="vm_vrouter_tenant", object_id=self.instance.id, content_type=self.instance.self_content_type_id)
-        if tags:
-            service_instances = self.stub.ServiceInstance.objects.filter(id=int(tags[0].value))
-            if not service_instances:
-                raise Exception("ServiceInstance %d linked to vsg %s does not exist" % (int(tags[0].value), self))
-            return service_instances[0].leaf_model.public_ip
-        else:
-            raise Exception("no vm_vrouter_tenant tag for instance %s" % self.instance)
-
-    @property
-    def wan_vm_mac(self):
-        tags = self.stub.Tag.objects.filter(name="vm_vrouter_tenant", object_id=self.instance.id, content_type=self.instance.self_content_type_id)
-        if tags:
-            service_instances = self.stub.ServiceInstance.objects.filter(id=int(tags[0].value))
-            if not service_instances:
-                raise Exception("ServiceInstance %d linked to vsg %s does not exist" % (int(tags[0].value), self))
-            return service_instances[0].leaf_model.public_mac
-        else:
-            raise Exception("no vm_vrouter_tenant tag for instance %s" % self.instance)
-
-
-register_convenience_wrapper("VSGTenant", ORMWrapperVSGServiceInstance)   # DEPRECATED
-register_convenience_wrapper("VSGServiceInstance", ORMWrapperVSGServiceInstance)
diff --git a/xos/xos_client/xosapi/convenience/vtrtenant.py b/xos/xos_client/xosapi/convenience/vtrtenant.py
deleted file mode 100644
index ddc64ae..0000000
--- a/xos/xos_client/xosapi/convenience/vtrtenant.py
+++ /dev/null
@@ -1,23 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# 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 xosapi.orm import ORMWrapper, register_convenience_wrapper
-
-class ORMWrapperVTRTenant(ORMWrapper):
-    def get_generic_foreignkeys(self):
-        return [{"name": "target", "content_type": "target_type", "id": "target_id"}]
-
-register_convenience_wrapper("VTRTenant", ORMWrapperVTRTenant)
diff --git a/xos/xos_client/xosapi/orm.py b/xos/xos_client/xosapi/orm.py
index a50b7ee..7869184 100644
--- a/xos/xos_client/xosapi/orm.py
+++ b/xos/xos_client/xosapi/orm.py
@@ -38,6 +38,8 @@
 import os
 import sys
 import time
+import imp
+import logging as log
 
 convenience_wrappers = {}
 
@@ -618,25 +620,40 @@
 
     return cls(wrapped_class, *args, **kwargs)
 
-import convenience.addresspool
-import convenience.privilege
-import convenience.instance
-import convenience.network
-import convenience.cordsubscriberroot
-import convenience.voltserviceinstance
-import convenience.vsgserviceinstance
-import convenience.serviceinstance
-import convenience.vrouterservice
-import convenience.vroutertenant
-import convenience.vrouterapp
-import convenience.service
-import convenience.onosapp
-import convenience.controller
-import convenience.user
-import convenience.slice
-import convenience.port
-import convenience.tag
-import convenience.vtrtenant
-import convenience.addressmanagerservice
-import convenience.addressmanagerserviceinstance
+def import_convenience_methods():
+
+    log.info("Loading convenience methods")
+
+    cwd = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+    api_convenience_dir = os.path.join(cwd, "convenience")
+    for file in os.listdir(api_convenience_dir):
+        if file.endswith(".py") and not "test" in file:
+            pathname = os.path.join(api_convenience_dir, file)
+            try:
+                log.debug("Loading: %s" % file)
+                imp.load_source(file[:-3], pathname)
+            except Exception, e:
+                log.error("Cannot import api convenience method for: %s, %s", (file[:-3], pathname))
+
+
+# import convenience.addresspool
+# import convenience.privilege
+# import convenience.instance
+# import convenience.network
+# import convenience.cordsubscriberroot
+# import convenience.vsgserviceinstance
+# import convenience.serviceinstance
+# import convenience.vrouterservice
+# import convenience.vroutertenant
+# import convenience.vrouterapp
+# import convenience.service
+# import convenience.onosapp
+# import convenience.controller
+# import convenience.user
+# import convenience.slice
+# import convenience.port
+# import convenience.tag
+# import convenience.vtrtenant
+# import convenience.addressmanagerservice
+# import convenience.addressmanagerserviceinstance
 
diff --git a/xos/xos_client/xosapi/xos_grpc_client.py b/xos/xos_client/xosapi/xos_grpc_client.py
index 46fb829..62b9a4a 100644
--- a/xos/xos_client/xosapi/xos_grpc_client.py
+++ b/xos/xos_client/xosapi/xos_grpc_client.py
@@ -33,7 +33,7 @@
 import chameleon.grpc_client.grpc_client as chameleon_client
 
 from twisted.internet import reactor
-
+from google.protobuf.empty_pb2 import Empty
 
 SERVER_CA="/usr/local/share/ca-certificates/local_certs.crt"
 
@@ -61,9 +61,30 @@
 
     def set_reconnect_callback(self, reconnect_callback):
         self.reconnect_callback2 = reconnect_callback
+
         return self
 
+    def load_convenience_methods(self):
+
+        convenience_methods_dir = "/usr/local/lib/python2.7/dist-packages/xosapi/convenience/"
+
+        try:
+            cms = self.dynamicload.GetConvenienceMethods(Empty())
+        except grpc._channel._Rendezvous, e:
+            code = e.code()
+            if code == grpc.StatusCode.UNAVAILABLE:
+                # NOTE if the core is not available, restart the synchronizer
+                os.execv(sys.executable, ['python'] + sys.argv)
+        print "Loading convenience methods: %s" % [m.filename for m in cms.convenience_methods]
+
+        for cm in cms.convenience_methods:
+            print "Saving %s" % cm.filename
+            save_path = os.path.join(convenience_methods_dir, cm.filename)
+            file(save_path, "w").write(cm.contents)
+
+
     def reconnected(self):
+
         for api in ['modeldefs', 'utility', 'xos', 'dynamicload']:
             pb2_file_name = os.path.join(self.work_dir, api + "_pb2.py")
             pb2_grpc_file_name = os.path.join(self.work_dir, api + "_pb2_grpc.py")
@@ -89,6 +110,12 @@
         if hasattr(self, "xos"):
             self.xos_orm = orm.ORMStub(self.xos, self.xos_pb2, "xos")
 
+        # ask the core for the convenience methods
+        self.load_convenience_methods()
+
+        # Load convenience methods after reconnect
+        orm.import_convenience_methods()
+
         if self.reconnect_callback2:
             self.reconnect_callback2()
 
@@ -122,19 +149,16 @@
 
     defs = {"grpc_insecure_endpoint": "xos-core.cord.lab:50055",
             "grpc_secure_endpoint": "xos-core.cord.lab:50051",
-            "consul": None}
+            "config": '/opt/xos/config.yml'}
 
-    _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
+    _help = 'Path to the config file (default: %s)' % defs['config']
     parser.add_argument(
-        '-C', '--consul', dest='consul', action='store',
-        default=defs['consul'],
+        '-C', '--config', dest='config', action='store',
+        default=defs['config'],
         help=_help)
 
-    _help = ('gRPC insecure end-point to connect to. It can either be a direct'
-             'definition in the form of <hostname>:<port>, or it can be an'
-             'indirect definition in the form of @<service-name> where'
-             '<service-name> is the name of the grpc service as registered'
-             'in consul (example: @voltha-grpc). (default: %s'
+    _help = ('gRPC insecure end-point to connect to. It is a direct',
+             '. (default: %s'
              % defs['grpc_insecure_endpoint'])
     parser.add_argument('-G', '--grpc-insecure-endpoint',
                         dest='grpc_insecure_endpoint',
@@ -142,11 +166,8 @@
                         default=defs["grpc_insecure_endpoint"],
                         help=_help)
 
-    _help = ('gRPC secure end-point to connect to. It can either be a direct'
-             'definition in the form of <hostname>:<port>, or it can be an'
-             'indirect definition in the form of @<service-name> where'
-             '<service-name> is the name of the grpc service as registered'
-             'in consul (example: @voltha-grpc). (default: %s'
+    _help = ('gRPC secure end-point to connect to. It is a direct',
+             '. (default: %s'
              % defs["grpc_secure_endpoint"])
     parser.add_argument('-S', '--grpc-secure-endpoint',
                         dest='grpc_secure_endpoint',
@@ -190,18 +211,27 @@
     return args
 
 def setup_logging(args):
-    import logging
-    import structlog
+    import os
 
-    verbosity_adjust = (args.verbose or 0) - (args.quiet or 0)
-    logging.basicConfig()
-    logger = logging.getLogger()
-    logger.setLevel(logging.DEBUG - 10*verbosity_adjust)
+    if os.path.isfile(args.config):
+        from xosconfig import Config
+        from multistructlog import create_logger
+        Config.init(args.config, 'synchronizer-config-schema.yaml')
+        log = create_logger(Config().get('logging'))
+    else:
+        import logging
+        import structlog
 
-    def logger_factory():
-        return logger
+        verbosity_adjust = (args.verbose or 0) - (args.quiet or 0)
+        logging.basicConfig()
+        logger = logging.getLogger()
+        logger.setLevel(logging.DEBUG - 10 * verbosity_adjust)
 
-    structlog.configure(logger_factory=logger_factory)
+        def logger_factory():
+            return logger
+
+        structlog.configure(logger_factory=logger_factory)
+
 
 def coreclient_reconnect(client, reconnect_callback, *args, **kwargs):
     global coreapi
diff --git a/xos/xos_client/xossh b/xos/xos_client/xossh
index 02bab8a..61bc651 100644
--- a/xos/xos_client/xossh
+++ b/xos/xos_client/xossh
@@ -19,21 +19,18 @@
 def parse_args():
     parser = argparse.ArgumentParser()
 
-    defs = {"grpc_insecure_endpoint": "xos-core.cord.lab:50055",
-            "grpc_secure_endpoint": "xos-core.cord.lab:50051",
-            "consul": None}
+    defs = {"grpc_insecure_endpoint": "xos-core:50055",
+            "grpc_secure_endpoint": "xos-core:50051",
+            "config": '/opt/xos/config.yml'}
 
-    _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
+    _help = 'Path to the config file (default: %s)' % defs['config']
     parser.add_argument(
-        '-C', '--consul', dest='consul', action='store',
-        default=defs['consul'],
+        '-C', '--config', dest='config', action='store',
+        default=defs['config'],
         help=_help)
 
-    _help = ('gRPC insecure end-point to connect to. It can either be a direct'
-             'definition in the form of <hostname>:<port>, or it can be an'
-             'indirect definition in the form of @<service-name> where'
-             '<service-name> is the name of the grpc service as registered'
-             'in consul (example: @voltha-grpc). (default: %s'
+    _help = ('gRPC insecure end-point to connect to. It is a direct',
+             '. (default: %s'
              % defs['grpc_insecure_endpoint'])
     parser.add_argument('-G', '--grpc-insecure-endpoint',
                         dest='grpc_insecure_endpoint',
@@ -41,11 +38,8 @@
                         default=defs["grpc_insecure_endpoint"],
                         help=_help)
 
-    _help = ('gRPC secure end-point to connect to. It can either be a direct'
-             'definition in the form of <hostname>:<port>, or it can be an'
-             'indirect definition in the form of @<service-name> where'
-             '<service-name> is the name of the grpc service as registered'
-             'in consul (example: @voltha-grpc). (default: %s'
+    _help = ('gRPC secure end-point to connect to. It is a direct',
+             '. (default: %s'
              % defs["grpc_secure_endpoint"])
     parser.add_argument('-S', '--grpc-secure-endpoint',
                         dest='grpc_secure_endpoint',